newsynth (empty) → 0.1.0.0
raw patch · 23 files changed
+5759/−0 lines, 23 filesdep +basedep +fixedprecdep +newsynthbuild-type:Customsetup-changedbinary-added
Dependencies added: base, fixedprec, newsynth, random, superdoc, time
Files
- ChangeLog +31/−0
- LICENSE +675/−0
- Quantum/Synthesis/ArcTan2.hs +36/−0
- Quantum/Synthesis/Clifford.hs +304/−0
- Quantum/Synthesis/CliffordT.hs +606/−0
- Quantum/Synthesis/EuclideanDomain.hs +153/−0
- Quantum/Synthesis/EulerAngles.hs +51/−0
- Quantum/Synthesis/LaTeX.hs +180/−0
- Quantum/Synthesis/Matrix.hs +601/−0
- Quantum/Synthesis/MultiQubitSynthesis.hs +468/−0
- Quantum/Synthesis/Newsynth.hs +453/−0
- Quantum/Synthesis/Ring.hs +1046/−0
- Quantum/Synthesis/Ring/FixedPrec.hs +26/−0
- Quantum/Synthesis/Ring/SymReal.hs +21/−0
- Quantum/Synthesis/RotationDecomposition.hs +160/−0
- Quantum/Synthesis/SymReal.hs +493/−0
- Setup.hs +2/−0
- images/E.png binary
- images/ERot_phase.png binary
- images/ERot_zx.png binary
- newsynth.cabal +102/−0
- programs/CommandLine.hs +81/−0
- programs/newsynth.hs +270/−0
+ ChangeLog view
@@ -0,0 +1,31 @@+ChangeLog++v0.1.0.0 2013/12/14+ (2013/12/13) PS1 - added new rings DComplex and QComplex.+ Improvements to the ring QOmega.+ (2013/12/13) PS1 - more uniform naming of rings. Old names+ DInteger, DReal, EReal, DComplex, EComplex have become ZRootTwo,+ DRootTwo, QRootTwo, DRComplex, and QRComplex, respectively.+ (2013/12/13) PS1 - adjusted output syntax to remove Unicode and+ make output parseable.+ (2013/12/13) PS1 - refactored as a Cabal package.+ (2013/12/11) PS1 - removed erroneous Adjoint2 instances for Double+ and Float.+ (2013/12/11) PS1 - removed dependency on numbers package, removed+ Random instance for FixedPrec (this is now in fixedprec package).+ (2013/12/11) PS1 - added EmptyDataDecls pragma, to keep GHC happy.+ (2013/12/10) PS1 - removed some unnecessary type class+ dependencies.+ (2013/09/25) PS1 - added "alternate" version of multi-qubit+ synthesis algorithm, using only generators of determinant 1 if+ possible.+ (2013/09/25) PS1 - renamed some constructors and deconstructors+ for matrices.++Release 2013/09/02+ Released as part of Quipper 0.5.+ (2013/07/08) PS1 - moved definitions of U2 and SO3 to Matrix.hs.+ (2013/07/05) PS1 - added RotationDecomposition module.++Release 2013/06/19+ Initial public release, as part of Quipper 0.4.
+ LICENSE view
@@ -0,0 +1,675 @@+ GNU GENERAL PUBLIC LICENSE+ Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The GNU General Public License is a free, copyleft license for+software and other kinds of works.++ The licenses for most software and other practical works are designed+to take away your freedom to share and change the works. By contrast,+the GNU General Public License is intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users. We, the Free Software Foundation, use the+GNU General Public License for most of our software; it applies also to+any other work released this way by its authors. You can apply it to+your programs, too.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+them if you wish), that you receive source code or can get it if you+want it, that you can change the software or use pieces of it in new+free programs, and that you know you can do these things.++ To protect your rights, we need to prevent others from denying you+these rights or asking you to surrender the rights. Therefore, you have+certain responsibilities if you distribute copies of the software, or if+you modify it: responsibilities to respect the freedom of others.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must pass on to the recipients the same+freedoms that you received. You must make sure that they, too, receive+or can get the source code. And you must show them these terms so they+know their rights.++ Developers that use the GNU GPL protect your rights with two steps:+(1) assert copyright on the software, and (2) offer you this License+giving you legal permission to copy, distribute and/or modify it.++ For the developers' and authors' protection, the GPL clearly explains+that there is no warranty for this free software. For both users' and+authors' sake, the GPL requires that modified versions be marked as+changed, so that their problems will not be attributed erroneously to+authors of previous versions.++ Some devices are designed to deny users access to install or run+modified versions of the software inside them, although the manufacturer+can do so. This is fundamentally incompatible with the aim of+protecting users' freedom to change the software. The systematic+pattern of such abuse occurs in the area of products for individuals to+use, which is precisely where it is most unacceptable. Therefore, we+have designed this version of the GPL to prohibit the practice for those+products. If such problems arise substantially in other domains, we+stand ready to extend this provision to those domains in future versions+of the GPL, as needed to protect the freedom of users.++ Finally, every program is threatened constantly by software patents.+States should not allow patents to restrict development and use of+software on general-purpose computers, but in those that do, we wish to+avoid the special danger that patents applied to a free program could+make it effectively proprietary. To prevent this, the GPL assures that+patents cannot be used to render the program non-free.++ The precise terms and conditions for copying, distribution and+modification follow.++ TERMS AND CONDITIONS++ 0. Definitions.++ "This License" refers to version 3 of the GNU General Public License.++ "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.+ + "The Program" refers to any copyrightable work licensed under this+License. Each licensee is addressed as "you". "Licensees" and+"recipients" may be individuals or organizations.++ To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy. The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++ A "covered work" means either the unmodified Program or a work based+on the Program.++ To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy. Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++ To "convey" a work means any kind of propagation that enables other+parties to make or receive copies. Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++ An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License. If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++ 1. Source Code.++ The "source code" for a work means the preferred form of the work+for making modifications to it. "Object code" means any non-source+form of a work.++ A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++ The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form. A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++ The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities. However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work. For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++ The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++ The Corresponding Source for a work in source code form is that+same work.++ 2. Basic Permissions.++ All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met. This License explicitly affirms your unlimited+permission to run the unmodified Program. The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work. This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++ You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force. You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright. Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++ Conveying under any other circumstances is permitted solely under+the conditions stated below. Sublicensing is not allowed; section 10+makes it unnecessary.++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.++ No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++ When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++ 4. Conveying Verbatim Copies.++ You may convey verbatim copies of the Program's source code as you+receive it, in any medium, provided that you conspicuously and+appropriately publish on each copy an appropriate copyright notice;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++ You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++ 5. Conveying Modified Source Versions.++ You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++ a) The work must carry prominent notices stating that you modified+ it, and giving a relevant date.++ b) The work must carry prominent notices stating that it is+ released under this License and any conditions added under section+ 7. This requirement modifies the requirement in section 4 to+ "keep intact all notices".++ c) You must license the entire work, as a whole, under this+ License to anyone who comes into possession of a copy. This+ License will therefore apply, along with any applicable section 7+ additional terms, to the whole of the work, and all its parts,+ regardless of how they are packaged. This License gives no+ permission to license the work in any other way, but it does not+ invalidate such permission if you have separately received it.++ d) If the work has interactive user interfaces, each must display+ Appropriate Legal Notices; however, if the Program has interactive+ interfaces that do not display Appropriate Legal Notices, your+ work need not make them do so.++ A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit. Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++ 6. Conveying Non-Source Forms.++ You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++ a) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by the+ Corresponding Source fixed on a durable physical medium+ customarily used for software interchange.++ b) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by a+ written offer, valid for at least three years and valid for as+ long as you offer spare parts or customer support for that product+ model, to give anyone who possesses the object code either (1) a+ copy of the Corresponding Source for all the software in the+ product that is covered by this License, on a durable physical+ medium customarily used for software interchange, for a price no+ more than your reasonable cost of physically performing this+ conveying of source, or (2) access to copy the+ Corresponding Source from a network server at no charge.++ c) Convey individual copies of the object code with a copy of the+ written offer to provide the Corresponding Source. This+ alternative is allowed only occasionally and noncommercially, and+ only if you received the object code with such an offer, in accord+ with subsection 6b.++ d) Convey the object code by offering access from a designated+ place (gratis or for a charge), and offer equivalent access to the+ Corresponding Source in the same way through the same place at no+ further charge. You need not require recipients to copy the+ Corresponding Source along with the object code. If the place to+ copy the object code is a network server, the Corresponding Source+ may be on a different server (operated by you or a third party)+ that supports equivalent copying facilities, provided you maintain+ clear directions next to the object code saying where to find the+ Corresponding Source. Regardless of what server hosts the+ Corresponding Source, you remain obligated to ensure that it is+ available for as long as needed to satisfy these requirements.++ e) Convey the object code using peer-to-peer transmission, provided+ you inform other peers where the object code and Corresponding+ Source of the work are being offered to the general public at no+ charge under subsection 6d.++ A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++ A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling. In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage. For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product. A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++ "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source. The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++ If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information. But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++ The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed. Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++ Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++ 7. Additional Terms.++ "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law. If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++ When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it. (Additional permissions may be written to require their own+removal in certain cases when you modify the work.) You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++ Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++ a) Disclaiming warranty or limiting liability differently from the+ terms of sections 15 and 16 of this License; or++ b) Requiring preservation of specified reasonable legal notices or+ author attributions in that material or in the Appropriate Legal+ Notices displayed by works containing it; or++ c) Prohibiting misrepresentation of the origin of that material, or+ requiring that modified versions of such material be marked in+ reasonable ways as different from the original version; or++ d) Limiting the use for publicity purposes of names of licensors or+ authors of the material; or++ e) Declining to grant rights under trademark law for use of some+ trade names, trademarks, or service marks; or++ f) Requiring indemnification of licensors and authors of that+ material by anyone who conveys the material (or modified versions of+ it) with contractual assumptions of liability to the recipient, for+ any liability that these contractual assumptions directly impose on+ those licensors and authors.++ All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10. If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term. If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++ If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++ Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++ 8. Termination.++ You may not propagate or modify a covered work except as expressly+provided under this License. Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++ However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++ Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++ Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License. If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++ 9. Acceptance Not Required for Having Copies.++ You are not required to accept this License in order to receive or+run a copy of the Program. Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance. However,+nothing other than this License grants you permission to propagate or+modify any covered work. These actions infringe copyright if you do+not accept this License. Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++ 10. Automatic Licensing of Downstream Recipients.++ Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License. You are not responsible+for enforcing compliance by third parties with this License.++ An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations. If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++ You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License. For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++ 11. Patents.++ A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based. The+work thus licensed is called the contributor's "contributor version".++ A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version. For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++ Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++ In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement). To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++ If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients. "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.+ + If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++ A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License. You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++ Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++ 12. No Surrender of Others' Freedom.++ If conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all. For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++ 13. Use with the GNU Affero General Public License.++ Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU Affero General Public License into a single+combined work, and to convey the resulting work. The terms of this+License will continue to apply to the part which is the covered work,+but the special requirements of the GNU Affero General Public License,+section 13, concerning interaction through a network will apply to the+combination as such.++ 14. Revised Versions of this License.++ The Free Software Foundation may publish revised and/or new versions of+the GNU General Public License from time to time. Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++ Each version is given a distinguishing version number. If the+Program specifies that a certain numbered version of the GNU General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation. If the Program does not specify a version number of the+GNU General Public License, you may choose any version ever published+by the Free Software Foundation.++ If the Program specifies that a proxy can decide which future+versions of the GNU General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++ Later license versions may give you additional or different+permissions. However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++ 15. Disclaimer of Warranty.++ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++ 16. Limitation of Liability.++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+SUCH DAMAGES.++ 17. Interpretation of Sections 15 and 16.++ If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++ If the program does terminal interaction, make it output a short+notice like this when it starts in an interactive mode:++ <program> Copyright (C) <year> <name of author>+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, your program's commands+might be different; for a GUI interface, you would use an "about box".++ You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU GPL, see+<http://www.gnu.org/licenses/>.++ The GNU General Public License does not permit incorporating your program+into proprietary programs. If your program is a subroutine library, you+may consider it more useful to permit linking proprietary applications with+the library. If this is what you want to do, use the GNU Lesser General+Public License instead of this License. But first, please read+<http://www.gnu.org/philosophy/why-not-lgpl.html>.+
+ Quantum/Synthesis/ArcTan2.hs view
@@ -0,0 +1,36 @@+-- | This module provides a replacement for Haskell's 'atan2'. The+-- problem is that Haskell's standard implementation of 'atan2'+-- depends on the 'RealFloat' class, which limits its applicability.+-- So we provide a new 'ArcTan2' class with an 'arctan2' function.+-- +-- Unlike Haskell's 'atan2', the 'arctan2' function may not take+-- signed zeros and signed infinities into account. But it works at+-- fixed-precision types such as 'FixedPrec'.++module Quantum.Synthesis.ArcTan2 where++import Data.Number.FixedPrec++-- ----------------------------------------------------------------------+-- * The arctan2 function++-- | We provide a replacement for Haskell's 'atan2', because the+-- latter depends on the 'RealFloat' class, which limits its+-- applicability.+class ArcTan2 a where+ arctan2 :: a -> a -> a+ +instance ArcTan2 Double where+ arctan2 = atan2++instance ArcTan2 Float where+ arctan2 = atan2++instance (Precision e) => ArcTan2 (FixedPrec e) where+ arctan2 y x+ | x == 0 && y == 0 = 0+ | abs y <= x = atan (y/x)+ | abs x <= y = pi/2 - atan (x/y)+ | abs x <= -y = -pi/2 - atan (x/y)+ | y >= 0 = pi + atan (y/x)+ | otherwise = -pi + atan (y/x)
+ Quantum/Synthesis/Clifford.hs view
@@ -0,0 +1,304 @@+{-# LANGUAGE OverlappingInstances #-}++{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}++-- | This module provides an efficient symbolic representation of the+-- Clifford group on one qubit. This group is generated by /S/, /H/,+-- and the scalar ω = [exp /i/π\/4]. It has 192 elements. ++module Quantum.Synthesis.Clifford (+ -- * The Clifford group+ Clifford,+ + -- ** Constructors+ clifford_X,+ clifford_Y,+ clifford_Z,+ clifford_H,+ clifford_S,+ clifford_SH,+ clifford_E,+ clifford_W,+ ToClifford(to_clifford),+ + -- ** Deconstructors+ clifford_decompose,+ Axis(..),+ clifford_decompose_coset,+ + -- ** Group operations+ clifford_id,+ clifford_mult,+ clifford_inv,+ + -- ** Conjugation by /T/+ clifford_tconj + ) where++-- ----------------------------------------------------------------------+-- * The Clifford group++-- $ We could, in principle, implement the Clifford group as an+-- enumerated type with 192 elements, and a large 192×192 lookup+-- table for the group multiplication. Instead, we take advantage of+-- some of the internal structure of the group to reduce the size of+-- the lookup tables. The resulting implementation is still very+-- efficient.++-- | A type representing single-qubit Clifford operators.+data Clifford = Clifford Int Int Int Int+ deriving (Eq, Ord)++instance Show Clifford where+ show (Clifford a b c d) = "C" ++ show a ++ show b ++ show c ++ show d++-- ----------------------------------------------------------------------+-- ** Constructors++-- | The Pauli /X/-gate as a Clifford operator.+clifford_X :: Clifford+clifford_X = Clifford 0 1 0 0++-- | The Pauli /Y/-gate as a Clifford operator.+clifford_Y :: Clifford+clifford_Y = Clifford 0 1 2 2++-- | The Pauli /Z/-gate as a Clifford operator.+clifford_Z :: Clifford+clifford_Z = Clifford 0 0 2 0++-- | The Hadamard gate as a Clifford operator.+clifford_H :: Clifford+clifford_H = Clifford 1 0 1 5++-- | The Clifford operator /S/.+clifford_S :: Clifford+clifford_S = Clifford 0 0 1 0++-- | The Clifford operator /SH/.+clifford_SH :: Clifford+clifford_SH = clifford_S `clifford_mult` clifford_H++-- | The Clifford operator /E/ = /H//S/[sup 3]ω[sup 3]. This operator is+-- uniquely determined by the properties /E/³ = /I/, +-- /EXE/⁻¹ = /Y/, /EYE/⁻¹ = /Z/, and /EZE/⁻¹ = /X/.+-- +-- \[image E.png]+clifford_E :: Clifford+clifford_E = Clifford 1 0 0 0++-- | The Clifford operator ω = [exp /i/π\/4].+clifford_W :: Clifford+clifford_W = Clifford 0 0 0 1++-- | A type class for things that can be exactly converted to a+-- Clifford operator. One particular instance of this is 'String', so+-- that Clifford operators can be denoted, e.g.,+-- +-- > to_clifford "-iX"+-- +-- The valid characters for such string conversions are @\"XYZHSEIWi-\"@.+class ToClifford a where+ -- | Convert any suitable thing to a Clifford operator.+ to_clifford :: a -> Clifford+ +instance ToClifford Clifford where+ to_clifford = id+ +instance ToClifford Char where+ to_clifford 'E' = clifford_E+ to_clifford 'X' = clifford_X+ to_clifford 'S' = clifford_S+ to_clifford 'W' = clifford_W+ to_clifford 'I' = clifford_id+ to_clifford 'i' = Clifford 0 0 0 2+ to_clifford '-' = Clifford 0 0 0 4+ to_clifford 'H' = clifford_H+ to_clifford 'Y' = clifford_Y+ to_clifford 'Z' = clifford_Z+ to_clifford x = error $ "ToClifford Char: unknown gate " ++ show x++instance ToClifford a => ToClifford [a] where+ to_clifford [] = clifford_id+ to_clifford (h:t) = to_clifford h `clifford_mult` to_clifford t++-- ----------------------------------------------------------------------+-- ** Deconstructors++-- | Given a Clifford operator /U/, return (/a/, /b/, /c/, /d/) such that+-- +-- * /U/ = /E/[sup /a/]/X/[sup /b/]/S/[sup /c/]ω[sup /d/],+-- +-- * /a/ ∈ {0, 1, 2}, /b/ ∈ {0, 1}, /c/ ∈ {0, …, 3}, and /d/ ∈ {0, …,+-- 7}.+-- +-- Here, /E/ = /H//S/[sup 3]ω[sup 3]. Note that /E/, /X/, /S/, and ω have order+-- 3, 2, 4, and 8, respectively. Moreover, each Clifford operator can+-- be uniquely represented as above.+clifford_decompose :: (ToClifford a) => a -> (Int, Int, Int, Int)+clifford_decompose m = (a,b,c,d) where+ Clifford a b c d = to_clifford m++-- | A axis is either /I/, /H/, or /SH/.+data Axis = Axis_I | Axis_H | Axis_SH+ deriving (Eq, Show)++instance ToClifford Axis where+ to_clifford Axis_I = to_clifford "I"+ to_clifford Axis_H = to_clifford "H"+ to_clifford Axis_SH = to_clifford "SH"++-- | Given a Clifford operator /U/, return (/K/, /b/, /c/, /d/) such that+-- +-- * /U/ = /K//X/[sup /b/]/S/[sup /c/]ω[sup /d/],+-- +-- * /K/ ∈ {/I/, /H/, /SH/}, /b/ ∈ {0, 1}, /c/ ∈ {0, …, 3}, and /d/ ∈ {0, …,+-- 7}.+clifford_decompose_coset :: (ToClifford a) => a -> (Axis, Int, Int, Int)+clifford_decompose_coset u = case op of+ Clifford 0 b c d -> (Axis_I, b, c, d)+ Clifford 1 b c d -> (Axis_H, b', c', d') where+ Clifford 0 b' c' d' = clifford_inv "H" `clifford_mult` op+ Clifford 2 b c d -> (Axis_SH, b', c', d') where+ Clifford 0 b' c' d' = clifford_inv "SH" `clifford_mult` op+ where+ op = to_clifford u+ +-- ----------------------------------------------------------------------+-- ** Group operations++-- | The identity Clifford operator.+clifford_id :: Clifford+clifford_id = Clifford 0 0 0 0++-- | Clifford multiplication.+clifford_mult :: Clifford -> Clifford -> Clifford+clifford_mult u1 u2 = u where+ -- U = U1 U2 + -- = A1 B1 C1 D1 A2 B2 C2 D2+ -- = A1 (B1 C1 A2) B2 C2 D1 D2+ -- = A1 (A3 B3 C3 D3) B2 C2 D1 D2+ -- = A1 A3 B3 (C3 B2) C2 D3 D1 D2+ -- = A1 A3 B3 (B2 C4 D4) C2 D3 D1 D2+ -- = (A1 A3) (B3 B2) (C4 C2) (D4 D3 D1 D2)+ -- = A B C D+ Clifford a1 b1 c1 d1 = u1+ Clifford a2 b2 c2 d2 = u2+ (a3, b3, c3, d3) = conj3 b1 c1 a2+ (c4, d4) = conj2 c3 b2+ a = (a1 + a3) `mod` 3+ b = (b3 + b2) `mod` 2+ c = (c4 + c2) `mod` 4+ d = (d4 + d3 + d1 + d2) `mod` 8+ u = Clifford a b c d++-- | Clifford inverse.+clifford_inv :: (ToClifford a) => a -> Clifford+clifford_inv op = Clifford a2 b2 c2 d3 where+ -- U⁻¹ = (A B C)⁻¹ D⁻¹ = (A2 B2 C2 D2) D⁻¹+ Clifford a b c d = to_clifford op+ (a2, b2, c2, d2) = cinv a b c+ d3 = (d2 - d) `mod` 8++-- ----------------------------------------------------------------------+-- ** Conjugation by /T/++-- | Given a Clifford gate /C/, return an axis /K/ ∈ {/I/, /H/, /SH/}+-- and a Clifford gate /C'/ such that+-- +-- * /C//T/ = /K//T//C/'.+clifford_tconj :: Clifford -> (Axis, Clifford)+clifford_tconj u = (k, v) where+ -- U T = A1 B1 C1 D1 T+ -- = (A1 B1 T) C1 D1+ -- = (K T B1 C2 D2) C1 D1+ -- = K T B1 (C2 C1) (D2 D1)+ Clifford a1 b1 c1 d1 = u+ (k, c2, d2) = tconj a1 b1+ c = (c2 + c1) `mod` 4+ d = (d2 + d1) `mod` 8+ v = Clifford 0 b1 c d++-- ----------------------------------------------------------------------+-- ** Lookup tables++-- | 'conj2' /c/ /b/ returns (/c/', /d/') such that+-- +-- * /S/[sup /c/]/X/[sup /b/] = /X/[sup /b/]/S/[sup /c/']ω[sup /d/'].+conj2 :: Int -> Int -> (Int, Int)+conj2 0 0 = (0,0)+conj2 0 1 = (0,0)+conj2 1 0 = (1,0)+conj2 1 1 = (3,2)+conj2 2 0 = (2,0)+conj2 2 1 = (2,4)+conj2 3 0 = (3,0)+conj2 3 1 = (1,6)++-- | 'conj3' /b/ /c/ /a/ returns (/a/', /b/', /c/', /d/') such that+-- +-- * /X/[sup /b/]/S/[sup /c/]/E/[sup /a/] = /E/[sup /a/']/X/[sup /b/']/S/[sup /c/']ω[sup /d/'].+conj3 :: Int -> Int -> Int -> (Int, Int, Int, Int)+conj3 0 0 0 = (0,0,0,0)+conj3 0 0 1 = (1,0,0,0)+conj3 0 0 2 = (2,0,0,0)+conj3 0 1 0 = (0,0,1,0)+conj3 0 1 1 = (2,0,3,6)+conj3 0 1 2 = (1,1,3,4)+conj3 0 2 0 = (0,0,2,0)+conj3 0 2 1 = (1,1,2,2)+conj3 0 2 2 = (2,1,0,0)+conj3 0 3 0 = (0,0,3,0)+conj3 0 3 1 = (2,1,3,6)+conj3 0 3 2 = (1,0,1,2)+conj3 1 0 0 = (0,1,0,0)+conj3 1 0 1 = (1,0,2,0)+conj3 1 0 2 = (2,1,2,2)+conj3 1 1 0 = (0,1,1,0)+conj3 1 1 1 = (2,1,1,0)+conj3 1 1 2 = (1,1,1,0)+conj3 1 2 0 = (0,1,2,0)+conj3 1 2 1 = (1,1,0,6)+conj3 1 2 2 = (2,0,2,6)+conj3 1 3 0 = (0,1,3,0)+conj3 1 3 1 = (2,0,1,4)+conj3 1 3 2 = (1,0,3,2)++-- | 'cinv' /a/ /b/ /c/ returns (/a/', /b/', /c/', /d/') such that+-- +-- * (/E/[sup /a/]/X/[sup /b/]/S/[sup /c/])⁻¹ = /E/[sup /a/']/X/[sup /b/']/S/[sup /c/']ω[sup /d/'].+cinv :: Int -> Int -> Int -> (Int, Int, Int, Int)+cinv 0 0 0 = (0,0,0,0)+cinv 0 0 1 = (0,0,3,0)+cinv 0 0 2 = (0,0,2,0)+cinv 0 0 3 = (0,0,1,0)+cinv 0 1 0 = (0,1,0,0)+cinv 0 1 1 = (0,1,1,6)+cinv 0 1 2 = (0,1,2,4)+cinv 0 1 3 = (0,1,3,2)+cinv 1 0 0 = (2,0,0,0)+cinv 1 0 1 = (1,0,1,2)+cinv 1 0 2 = (2,1,0,0)+cinv 1 0 3 = (1,1,3,4)+cinv 1 1 0 = (2,1,2,2)+cinv 1 1 1 = (1,1,1,6)+cinv 1 1 2 = (2,0,2,2)+cinv 1 1 3 = (1,0,3,4)+cinv 2 0 0 = (1,0,0,0)+cinv 2 0 1 = (2,1,3,6)+cinv 2 0 2 = (1,1,2,2)+cinv 2 0 3 = (2,0,3,6)+cinv 2 1 0 = (1,0,2,0)+cinv 2 1 1 = (2,1,1,6)+cinv 2 1 2 = (1,1,0,2)+cinv 2 1 3 = (2,0,1,6)++-- | 'tconj2' /a/ /b/ returns (/K/, /c/, /d/) such that+-- +-- * /E/[sup /a/]/X/[sup /b/]/T/ = /K//T//X/[sup /b/]/S/[sup /c/]ω[sup /d/].+tconj 0 0 = (Axis_I, 0, 0)+tconj 0 1 = (Axis_I, 1, 7)+tconj 1 0 = (Axis_H, 3, 3)+tconj 1 1 = (Axis_H, 2, 0)+tconj 2 0 = (Axis_SH, 0, 5)+tconj 2 1 = (Axis_SH, 1, 4)
+ Quantum/Synthesis/CliffordT.hs view
@@ -0,0 +1,606 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE FlexibleContexts #-}++-- | This module provides a representation of the single-qubit+-- Clifford+/T/ operators, Matsumoto-Amano normal forms, and functions+-- for the exact synthesis of single-qubit Clifford+/T/ operators.+--+-- Matsumoto-Amano normal forms and the Matsumoto-Amano exact+-- synthesis algorithm are described in the paper:+--+-- * Ken Matsumoto, Kazuyuki Amano. Representation of Quantum Circuits+-- with Clifford and π\/8 Gates. <http://arxiv.org/abs/0806.3834>.++module Quantum.Synthesis.CliffordT where++import Quantum.Synthesis.Ring+import Quantum.Synthesis.Matrix+import Quantum.Synthesis.Clifford+import Quantum.Synthesis.MultiQubitSynthesis++import Data.List+import Data.Bits++-- ----------------------------------------------------------------------+-- * Clifford+/T/ interchange format++-- $ It is convenient to have a simple but exact \"interchange+-- format\" for operators in the single-qubit Clifford+/T/+-- group. Different operator representations can be converted to and+-- from this format.+--+-- Our format is simply a list of gates from /X/, /Y/, /Z/, /H/, /S/,+-- /T/, and /E/ = /H//S/[sup 3]ω[sup 3], with the obvious+-- interpretation as a matrix product. We also include the global+-- phase gate /W/ = ω = [exp /i/π\/4]. The /W/ gate is ignored when+-- converting to or from representations that cannot represent global+-- phase (such as the Bloch sphere representation).++-- | An enumeration type to represent symbolic basic gates (/X/, /Y/,+-- /Z/, /H/, /S/, /T/, /W/, /E/).+-- +-- Note: when we use a list of 'Gate's to express a sequence of+-- operators, the operators are meant to be applied right-to-left,+-- i.e., as in the mathematical notation for matrix multiplication.+-- This is the opposite of the quantum circuit notation.+data Gate = X | Y | Z | H | S | T | E | W+ deriving (Show, Eq)++-- | A type class for all things that can be exactly converted to a+-- list of gates. These are the exact representations of the+-- single-qubit Clifford+/T/ group.+class ToGates a where+ -- | Convert any suitable thing to a list of gates.+ to_gates :: a -> [Gate]++instance ToGates Gate where+ to_gates x = [x]++instance (ToGates a) => ToGates [a] where+ to_gates x = concat [ to_gates y | y <- x ]++instance ToGates Char where+ to_gates 'X' = [X]+ to_gates 'Y' = [Y]+ to_gates 'Z' = [Z]+ to_gates 'H' = [H]+ to_gates 'S' = [S]+ to_gates 'T' = [T]+ to_gates 'E' = [E]+ to_gates 'W' = [W]+ to_gates 'I' = []+ to_gates '-' = [W,W,W,W]+ to_gates 'i' = [W,W]+ to_gates x = error $ "to_gates[Char]: undefined -- " ++ (show x)++instance ToGates Axis where+ to_gates Axis_I = []+ to_gates Axis_H = [H]+ to_gates Axis_SH = [S,H]++instance ToGates Clifford where+ to_gates op = as ++ xs ++ ss ++ ws where+ (k, b, c, d) = clifford_decompose_coset op+ as = to_gates k+ xs = replicate b X+ ss = replicate c S+ ws = replicate d W++-- | A type class for all things that a list of gates can be converted+-- to. For example, a list of gates can be converted to an element of+-- /U/(2) or an element of /SO/(3), using various (exact or+-- approximate) representations of the matrix entries.+class FromGates a where+ -- | Convert a list of gates to any suitable type.+ from_gates :: [Gate] -> a++instance FromGates String where+ from_gates = concat . map show++instance FromGates [Gate] where+ from_gates = id++-- | Invert a gate list.+invert_gates :: [Gate] -> [Gate]+invert_gates gs = aux [] gs where+ aux acc [] = acc+ aux acc (X:t) = aux (X:acc) t+ aux acc (Y:t) = aux (Y:acc) t+ aux acc (Z:t) = aux (Z:acc) t+ aux acc (H:t) = aux (H:acc) t+ aux acc (S:t) = aux (Z:S:acc) t+ aux acc (T:t) = aux (Z:S:T:acc) t+ aux acc (E:t) = aux (E:E:acc) t+ aux acc (W:t) = aux (W:W:W:W:W:W:W:acc) t++-- | Convert any precise format to any format.+convert :: (ToGates a, FromGates b) => a -> b+convert = from_gates . to_gates++-- ----------------------------------------------------------------------+-- * Matrices in /U/(2) and /SO/(3)++-- ----------------------------------------------------------------------+-- ** Matrices in /U/(2)++-- | The Pauli /X/ operator.+u2_X :: (Ring a) => U2 a+u2_X = matrix2x2 (0, 1)+ (1, 0)++-- | The Pauli /Y/ operator.+u2_Y :: (ComplexRing a) => U2 a+u2_Y = matrix2x2 (0, -i)+ (i, 0)++-- | The Pauli /Z/ operator.+u2_Z :: (Ring a) => U2 a+u2_Z = matrix2x2 (1, 0)+ (0, -1)++-- | The Hadamard operator.+u2_H :: (RootHalfRing a) => U2 a+u2_H = roothalf * matrix2x2 (1, 1)+ (1, -1)++-- | The /S/ operator.+u2_S :: (ComplexRing a) => U2 a+u2_S = matrix2x2 (1, 0)+ (0, i)++-- | The /T/ operator.+u2_T :: (OmegaRing a) => U2 a+u2_T = matrix2x2 (1, 0)+ (0, omega)++-- | The /E/ operator.+u2_E :: (OmegaRing a, RootHalfRing a) => U2 a+u2_E = roothalf * matrix2x2 (omega^3, omega)+ (omega^3, -omega)++-- | The /W/ = [exp /i/π\/4] global phase operator.+u2_W :: (OmegaRing a) => U2 a+u2_W = matrix2x2 (omega, 0)+ (0, omega)++-- | Convert a symbolic gate to the corresponding operator.+u2_of_gate :: (RootHalfRing a, ComplexRing a) => Gate -> U2 a+u2_of_gate X = u2_X+u2_of_gate Y = u2_Y+u2_of_gate Z = u2_Z+u2_of_gate H = u2_H+u2_of_gate S = u2_S+u2_of_gate T = u2_T+u2_of_gate E = u2_E+u2_of_gate W = u2_W++instance (RootHalfRing a, ComplexRing a) => FromGates (U2 a) where+ from_gates = product' . map u2_of_gate where+ product' = foldl' (*) 1++-- ----------------------------------------------------------------------+-- ** Matrices in /SO/(3)++-- $ This is the Bloch sphere representation of single qubit+-- operators.++-- | The Pauli /X/ operator.+so3_X :: (Ring a) => SO3 a+so3_X = matrix3x3 (1, 0, 0)+ (0, -1, 0)+ (0, 0, -1)++-- | The Pauli /Y/ operator.+so3_Y :: (Ring a) => SO3 a+so3_Y = matrix3x3 (-1, 0, 0)+ ( 0, 1, 0)+ ( 0, 0, -1)++-- | The Pauli /Z/ operator.+so3_Z :: (Ring a) => SO3 a+so3_Z = matrix3x3 (-1, 0, 0)+ ( 0, -1, 0)+ ( 0, 0, 1)++-- | The Hadamard operator.+so3_H :: (Ring a) => SO3 a+so3_H = matrix3x3 (0, 0, 1)+ (0, -1, 0)+ (1, 0, 0)++-- | The operator /S/.+so3_S :: (Ring a) => SO3 a+so3_S = matrix3x3 (0, -1, 0)+ (1, 0, 0)+ (0, 0, 1)++-- | The operator /E/.+so3_E :: (Ring a) => SO3 a+so3_E = matrix3x3 (0, 0, 1)+ (1, 0, 0)+ (0, 1, 0)++-- | The /T/ operator.+so3_T :: (RootHalfRing a) => SO3 a+so3_T = matrix3x3 (r, -r, 0)+ (r, r, 0)+ (0, 0, 1)+ where r = roothalf++-- | Convert a symbolic gate to the corresponding Bloch sphere+-- operator.+so3_of_gate :: (RootHalfRing a) => Gate -> SO3 a+so3_of_gate X = so3_X+so3_of_gate Y = so3_Y+so3_of_gate Z = so3_Z+so3_of_gate H = so3_H+so3_of_gate S = so3_S+so3_of_gate T = so3_T+so3_of_gate E = so3_E+so3_of_gate W = 1++instance (RootHalfRing a) => FromGates (SO3 a) where+ from_gates = product . map so3_of_gate++-- ----------------------------------------------------------------------+-- ** Conversions++-- | Conversion from /U/(2) to /SO/(3).+so3_of_u2 :: (Adjoint a, ComplexRing a, RealPart a b, HalfRing b) => U2 a -> SO3 b+so3_of_u2 u = matrix_of_function f where+ f i j = half * (real $ tr (sigma i * u * sigma j * adj u))+ sigma 0 = u2_X+ sigma 1 = u2_Y+ sigma 2 = u2_Z+ sigma _ = error "so3_of_u2" -- not reached++-- | Convert a Clifford operator to a matrix in /SO/(3).+so3_of_clifford :: (ToClifford a, Ring b) => a -> SO3 b+so3_of_clifford m = so3_E^a * so3_X^b * so3_S^c where+ (a,b,c,d) = clifford_decompose m++-- | Convert a matrix in /SO/(3) to a Clifford gate. Throw an error if+-- the matrix isn't Clifford.+clifford_of_so3 :: (Ring a, Eq a, Adjoint a) => SO3 a -> Clifford+clifford_of_so3 m = case columns_of_matrix m of+ [_, _, [ 1, 0, 0]] -> with "H"+ [_, _, [-1, 0, 0]] -> with "HX"+ [_, _, [ 0, 1, 0]] -> with "SH"+ [_, _, [ 0,-1, 0]] -> with "SHX"+ [_, _, [ 0, 0,-1]] -> with "X"+ [_, [-1, 0, 0], _] -> with "S"+ [_, [ 0,-1, 0], _] -> with "SS"+ [_, [ 1, 0, 0], _] -> with "SSS"+ [[1, 0, 0], [0, 1, 0], [0, 0, 1]] -> clifford_id+ _ -> error "clifford_of_so3: not a Clifford operator"+ where+ with s = op `clifford_mult` op1 where+ op = to_clifford s+ m1 = adj (so3_of_clifford op) * m+ op1 = clifford_of_so3 m1++instance (Ring a, Eq a, Adjoint a) => ToClifford (SO3 a) where+ to_clifford = clifford_of_so3++-- ----------------------------------------------------------------------+-- * Matsumoto-Amano normal forms++-- $ A Matsumoto-Amano normal form is a sequence of Clifford+/T/+-- operators that is of the form+--+-- * (ε | /T/) (/HT/ | /SHT/)[sup *] /C/.+--+-- Here, ε is the empty sequence, /C/ is any Clifford operator, and+-- the meanings of @\"|\"@ and @\"*\"@ are as for regular+-- expressions. Every single-qubit Clifford+/T/ operator has a unique+-- Matsumoto-Amano normal form.++-- ----------------------------------------------------------------------+-- ** Representation of normal forms++-- | A representation of normal forms, optimized for right+-- multiplication.+data NormalForm = NormalForm Syllables Clifford+ deriving (Eq)++-- | Syllables is a circuit of the form (ε|/T/) (/HT/|/SHT/)[sup *].+data Syllables =+ S_I -- ^ The empty sequence ε.+ | S_T -- ^ The sequence /T/.+ | SApp_HT Syllables -- ^ A sequence of the form …/HT/.+ | SApp_SHT Syllables -- ^ A sequence of the form …/SHT/.+ deriving (Eq, Show)++instance ToGates NormalForm where+ to_gates (NormalForm ts c) = to_gates ts ++ to_gates c++instance ToGates Syllables where+ to_gates S_I = []+ to_gates S_T = [T]+ to_gates (SApp_HT ts) = to_gates ts ++ [H, T]+ to_gates (SApp_SHT ts) = to_gates ts ++ [S, H, T]++instance Show NormalForm where+ show x = case to_gates x of+ [] -> "I"+ gs -> concat $ map show gs++-- | Right-multiply the given normal form by a gate.+normalform_append :: NormalForm -> Gate -> NormalForm+normalform_append (NormalForm ts c) X =+ NormalForm ts (c `clifford_mult` clifford_X)+normalform_append (NormalForm ts c) Y =+ NormalForm ts (c `clifford_mult` clifford_Y)+normalform_append (NormalForm ts c) Z =+ NormalForm ts (c `clifford_mult` clifford_Z)+normalform_append (NormalForm ts c) H =+ NormalForm ts (c `clifford_mult` clifford_H)+normalform_append (NormalForm ts c) S =+ NormalForm ts (c `clifford_mult` clifford_S)+normalform_append (NormalForm ts c) E =+ NormalForm ts (c `clifford_mult` clifford_E)+normalform_append (NormalForm ts c) W =+ NormalForm ts (c `clifford_mult` clifford_W)+normalform_append (NormalForm ts c) T+ | k == Axis_H = NormalForm (SApp_HT ts) c'+ | k == Axis_SH = NormalForm (SApp_SHT ts) c'+ | otherwise = case ts of+ S_I -> NormalForm S_T c'+ S_T -> NormalForm S_I (clifford_S `clifford_mult` c')+ SApp_HT ts' -> NormalForm ts' (clifford_HS `clifford_mult` c')+ SApp_SHT ts' -> NormalForm ts' (clifford_SHS `clifford_mult` c')+ where+ (k, c') = clifford_tconj c+ clifford_HS = to_clifford "HS"+ clifford_SHS = to_clifford "SHS"++-- ----------------------------------------------------------------------+-- ** Group operations on normal forms++-- | The identity as a normal form.+nf_id :: NormalForm+nf_id = NormalForm S_I clifford_id++-- | Multiply two normal forms. The right factor can be any+-- 'ToGates'.+nf_mult :: (ToGates b) => NormalForm -> b -> NormalForm+nf_mult a b = foldl' normalform_append a (to_gates b)++-- | Invert a normal form. The input can be any 'ToGates'.+nf_inv :: (ToGates a) => a -> NormalForm+nf_inv = from_gates . invert_gates . to_gates++-- ----------------------------------------------------------------------+-- ** Conversion to normal form++-- | Convert any 'ToGates' list to a 'NormalForm', thereby normalizing it.+normalize :: (ToGates a) => a -> NormalForm+normalize = nf_mult nf_id++instance FromGates NormalForm where+ from_gates = normalize++-- ----------------------------------------------------------------------+-- * Exact synthesis++-- ----------------------------------------------------------------------+-- ** Synthesis from /SO/(3)++-- | Input an exact matrix in /SO/(3), and output the corresponding+-- Clifford+/T/ normal form. It is an error if the given matrix is not+-- an element of /SO/(3), i.e., orthogonal with determinant 1.+--+-- This implementation uses the Matsumoto-Amano algorithm.+-- +-- Note: the list of gates will be returned in right-to-left order,+-- i.e., as in the mathematical notation for matrix multiplication.+-- This is the opposite of the quantum circuit notation.+synthesis_bloch :: SO3 DRootTwo -> [Gate]+synthesis_bloch m = aux m1 k+ where+ (m1, k) = denomexp_decompose m++ aux :: SO3 ZRootTwo -> Integer -> [Gate] + aux m 0 = to_gates (clifford_of_so3 m)+ aux m k = to_gates axis ++ [T] ++ aux m4 (k-1)+ where+ Matrix p = matrix_map parity m+ v1 = vector_head p+ v2 = vector_head (vector_tail p)+ v = list_of_vector $ vector_zipwith (\x y -> x + y - x*y) v1 v2+ axis = case v of+ [1, 1, 0] -> Axis_I+ [0, 1, 1] -> Axis_H+ [1, 0, 1] -> Axis_SH+ _ -> error "synthesis_bloch: not unitary"+ m2 = adj (so3_of_clifford axis) * m+ m3 = adj sqrt2T * m2+ m4 = matrix_map half_ZRootTwo m3+ sqrt2T = matrix3x3 (1, -1, 0) (1, 1, 0) (0, 0, roottwo)++ -- Divide a 'ZRootTwo' of the form 2/a/ + 2/b/√2 by 2, or throw an+ -- error if it is not of the required form.+ half_ZRootTwo :: ZRootTwo -> ZRootTwo+ half_ZRootTwo (RootTwo a b)+ | even a && even b = RootTwo a' b'+ | otherwise = error "synthesis_bloch: not unitary"+ where+ a' = a `div` 2+ b' = b `div` 2++instance (ToQOmega a) => ToGates (SO3 a) where+ to_gates = synthesis_bloch . matrix_map (to_dyadic . to_real . toQOmega)+ where+ to_real :: QOmega -> QRootTwo+ to_real x = case fromQOmega x :: QRComplex of + (Cplx a 0) -> a+ _ -> error "to_gates: not a real number"++-- ----------------------------------------------------------------------+-- ** Synthesis from /U/(2)++instance ToGates TwoLevel where+ to_gates (TL_X 0 1) = [X]+ to_gates (TL_X 1 0) = [X]+ to_gates (TL_H 0 1) = [H]+ to_gates (TL_H 1 0) = [X,H,X]+ to_gates (TL_T k 0 1)+ | k `mod` 2 == 1 = [T] ++ to_gates (TL_T (k-1) 0 1)+ | k `mod` 4 == 2 = [S] ++ to_gates (TL_T (k-2) 0 1)+ | k `mod` 8 == 4 = [Z]+ | otherwise = []+ to_gates (TL_T k 1 0) = [X] ++ to_gates (TL_T k 0 1) ++ [X]+ to_gates (TL_omega k 1) = to_gates (TL_T k 0 1)+ to_gates (TL_omega k 0) = to_gates (TL_T k 1 0)+ to_gates _ = error $ "ToGates TwoLevel: invalid gate"++-- | Input an exact matrix in /U/(2), and output the corresponding+-- Clifford+/T/ normal form. The behavior is undefined if the given+-- matrix is not an element of /U/(2), i.e., unitary with determinant+-- 1.+--+-- We use a variant of the Kliuchnikov-Maslov-Mosca algorithm, as+-- implemented in "Quantum.Synthesis.MultiQubitSynthesis".+-- +-- Note: the list of gates will be returned in right-to-left order,+-- i.e., as in the mathematical notation for matrix multiplication.+-- This is the opposite of the quantum circuit notation.+synthesis_u2 :: U2 DOmega -> [Gate]+synthesis_u2 = to_gates . normalize . synthesis_nqubit++instance (ToQOmega a) => ToGates (U2 a) where+ to_gates = synthesis_u2 . matrix_map (fromDOmega . to_dyadic . toQOmega)++-- ----------------------------------------------------------------------+-- * Compact representation of normal forms++-- $ It is sometimes useful to store Clifford+/T/ operators in a file;+-- for this purpose, we provide a very succinct encoding of+-- Clifford+/T/ operators as bit strings, which are in turns+-- represented as integers.+--+-- Our bitwise encoding is as follows. The first regular expression+-- represents the set of Matsumoto-Amano normal forms (with a+-- particular presentation of the rightmost Clifford operator). The+-- second regular expression, which has the same form, defines the+-- corresponding bit string encoding.+--+-- * (ε|/T/) (/HT/|/SHT/)[sup *] (ε|/H/|/SH/) (ε|/X/) (ε|/S²/) (ε|/S/) (ε|ω⁴) (ε|ω²) (ε|ω)+--+-- * (10|11) (0|1)[sup *] (00|01|10) (0|1) (0|1) (0|1) (0|1) (0|1) (0|1)+--+-- As a special case, the leading bits 10 are omitted in case the+-- encoded operator is a Clifford operator. This ensures that the+-- encoding of a Clifford operator is an integer from 0 to 191.+-- +-- This format has the property that the encoded Clifford+/T/+-- operator can, in principle, be read off directly from the hexadecimal+-- representation of the bit string, with the following decoding:+--+-- Leftmost one or two hexadecimal digits:+--+-- > 0 = n/a 4 = HT 8 = HTHT c = THTHT+-- > 1 = see below 5 = SHT 9 = HTSHT d = THTSHT+-- > 2 = ε 6 = THT a = SHTHT e = TSHTHT+-- > 3 = T 7 = TSHT b = SHTSHT f = TSHTSHT+-- >+-- > 10 = HTHTHT 14 = SHTHTHT 18 = THTHTHT 1c = TSHTHTHT+-- > 11 = HTHTSHT 15 = SHTHTSHT 19 = THTHTSHT 1d = TSHTHTSHT+-- > 12 = HTSHTHT 16 = SHTSHTHT 1a = THTSHTHT 1e = TSHTSHTHT+-- > 13 = HTSHTSHT 17 = SHTSHTSHT 1b = THTSHTSHT 1f = TSHTSHTSHT+--+-- Central hexadecimal digit:+--+-- > 0 = HTHTHTHT 4 = HTSHTHTHT 8 = SHTHTHTHT c = SHTSHTHTHT+-- > 1 = HTHTHTSHT 5 = HTSHTHTSHT 9 = SHTHTHTSHT d = SHTSHTHTSHT+-- > 2 = HTHTSHTHT 6 = HTSHTSHTHT a = SHTHTSHTHT e = SHTSHTSHTHT+-- > 3 = HTHTSHTSHT 7 = HTSHTSHTSHT b = SHTHTSHTSHT f = SHTSHTSHTSHT+--+-- Second-to-rightmost hexadecimal digit:+--+-- > 0 = ε 4 = H 8 = SH c = n/a+-- > 1 = SS 5 = HSS 9 = SHSS d = n/a+-- > 2 = X 6 = HX a = SHX e = n/a+-- > 3 = XSS 7 = HXSS b = SHXSS f = n/a+--+-- Rightmost hexadecimal digit:+--+-- > 0 = ε 4 = ω⁴ 8 = S c = Sω⁴+-- > 1 = ω 5 = ω⁵ 9 = Sω d = Sω⁵+-- > 2 = ω² 6 = ω⁶ a = Sω² e = Sω⁶+-- > 3 = ω³ 7 = ω⁷ b = Sω³ f = Sω⁷+--+-- For example, the hexadecimal integer+--+-- > 6bf723e31+--+-- encodes the Clifford+/T/ operator+--+-- > THT SHTHTSHTSHT SHTSHTSHTSHT HTSHTSHTSHT HTHTSHTHT HTHTSHTSHT SHTSHTSHTHT XSS ω.++-- | Compactly encode a 'NormalForm' as an 'Integer'.+normalform_pack :: NormalForm -> Integer+normalform_pack (NormalForm S_I op) = clifford_pack op+normalform_pack (NormalForm s op) = 256 * syllables_pack s + clifford_pack op+ where+ syllables_pack :: Syllables -> Integer+ syllables_pack S_I = 2+ syllables_pack S_T = 3+ syllables_pack (SApp_HT s) = (syllables_pack s `shiftL` 1) + 0+ syllables_pack (SApp_SHT s) = (syllables_pack s `shiftL` 1) + 1++-- | Decode a 'NormalForm' from its 'Integer' encoding. This is the+-- inverse of 'normalform_pack'.+normalform_unpack :: Integer -> NormalForm+normalform_unpack n + | n < 0 = error "normalform_unpack: invalid encoding"+ | n < 192 = NormalForm S_I op+ | n < 768 = error "normalform_unpack: invalid encoding"+ | otherwise = NormalForm s op + where+ s = syllables_unpack (n `shiftR` 8)+ op = clifford_unpack (n .&. 0xff)++ syllables_unpack :: Integer -> Syllables+ syllables_unpack 0 = error "normalform_unpack: invalid encoding"+ syllables_unpack 1 = error "normalform_unpack: invalid encoding"+ syllables_unpack 2 = S_I+ syllables_unpack 3 = S_T+ syllables_unpack n+ | even n = SApp_HT s+ | otherwise = SApp_SHT s+ where+ s = syllables_unpack (n `shiftR` 1)++-- | Encode a Clifford operator as an integer in the range 0−191.+clifford_pack :: Clifford -> Integer+clifford_pack op = toInteger (64 * encode k + 32*b + 8*c + d)+ where+ (k, b, c, d) = clifford_decompose_coset op+ encode Axis_I = 0+ encode Axis_H = 1+ encode Axis_SH = 2++-- | Decode a Clifford operator from its integer encoding. This is the+-- inverse of 'clifford_pack'+clifford_unpack :: Integer -> Clifford+clifford_unpack n + | n < 0 || n > 191 = error "clifford_unpack: invalid encoding"+ | otherwise = decode k * (clifford_X^b) * (clifford_S^c) * (clifford_W^d)+ where+ d = n .&. 0x7+ c = (n `shiftR` 3) .&. 0x3+ b = (n `shiftR` 5) .&. 0x1+ k = (n `shiftR` 6) .&. 0x3+ decode 0 = clifford_id+ decode 1 = clifford_H+ decode _ = clifford_SH+ (*) = clifford_mult+ (^) x n = foldl (*) clifford_id (genericReplicate n x)++instance ToGates Integer where+ to_gates = to_gates . normalform_unpack++instance FromGates Integer where+ from_gates = normalform_pack . from_gates
+ Quantum/Synthesis/EuclideanDomain.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE FlexibleInstances #-}++-- | This module provides a type class for Euclidean domains. A+-- Euclidean domain is a ring with a notion of division with+-- remainder, and therefore greatest common divisors.++module Quantum.Synthesis.EuclideanDomain where++import Quantum.Synthesis.Ring+import Data.Maybe++-- ----------------------------------------------------------------------+-- * Euclidean domains++-- ----------------------------------------------------------------------+-- ** Definition++-- | A type class for Euclidean domains. A Euclidean domain is a ring+-- with a Euclidean function and a division with remainder.+class (Eq a, Ring a) => EuclideanDomain a where+ -- | The Euclidean function for the Euclidean domain. This is a+ -- function /rank/ : /R/\\{0} → ℕ such that:+ -- + -- * for all nonzero /a/, /b/ ∈ /R/, /rank/(/a/) ≤ /rank/(/ab/);+ -- + -- * if /b/ ≠ 0 and (/q/,/r/) = /a/ `divmod` /b/, then either /r/ =+ -- 0 or /rank/(/r/) < /rank/(/b/).+ rank :: a -> Integer+ -- | Given /a/ and /b/≠0, return a quotient and remainder for+ -- division of /a/ by /b/. Specifically, return (/q/,/r/) such that+ -- /a/ = /qb/ + /r/, and such that /r/ = 0 or /rank/(/r/) < /rank/(/b/).+ divmod :: a -> a -> (a,a)++-- ----------------------------------------------------------------------+-- Particular Euclidean domains++instance EuclideanDomain Integer where+ rank x = x+ divmod x y = divMod x y++instance EuclideanDomain ZComplex where+ rank x = abs (norm x)+ divmod x y = (q, r) where+ (Cplx l m) = x * adj y+ k = norm y+ q1 = l `rounddiv` k+ q2 = m `rounddiv` k+ q = Cplx q1 q2+ r = x - y * q++instance EuclideanDomain ZRootTwo where+ rank x = abs (norm x)+ divmod x y@(RootTwo c d) = (q, r) where+ (RootTwo l m) = x * adj2 y+ k = norm y+ q1 = l `rounddiv` k+ q2 = m `rounddiv` k+ q = RootTwo q1 q2+ r = x - y * q+ +instance EuclideanDomain ZOmega where+ rank x = abs (norm x)+ divmod x y = (q, r) where+ (Omega a' b' c' d') = x * adj y * adj2(y * adj y)+ k = norm y+ a = a' `rounddiv` k+ b = b' `rounddiv` k+ c = c' `rounddiv` k+ d = d' `rounddiv` k+ q = Omega a b c d+ r = x - y * q ++-- ----------------------------------------------------------------------+-- ** Functions++-- | Calculate the remainder for the division of /x/ by /y/.+euclid_mod :: (EuclideanDomain a) => a -> a -> a+euclid_mod x y = r where+ (q,r) = x `divmod` y++infixl 7 `euclid_mod`++-- | Calculate the quotient for the division of /x/ by /y/, ignoring+-- the remainder, if any. This is typically, but not always, used in+-- situations where the remainder is known to be 0 ahead of time.+euclid_div :: (EuclideanDomain a) => a -> a -> a+euclid_div x y = q where+ (q,r) = x `divmod` y++infixl 7 `euclid_div`++-- | Calculate the greatest common divisor in any Euclidean domain.+euclid_gcd :: (EuclideanDomain a) => a -> a -> a+euclid_gcd x y+ | y == 0 = x+ | otherwise = euclid_gcd y r where+ (_,r) = divmod x y++-- | Perform the extended Euclidean algorithm. On inputs /x/ and+-- /y/, this returns (/a/,/b/,/s/,/t/,/d/) such that:+-- +-- * /d/ = gcd(/x/,/y/),+-- +-- * /ax/ + /by/ = /d/,+-- +-- * /sx/ + /ty/ = 0,+-- +-- * /at/ - /bs/ = 1.+extended_euclid :: (EuclideanDomain a) => a -> a -> (a, a, a, a, a)+extended_euclid x y+ | y == 0 = (1, 0, 0, 1, x)+ | otherwise = (b',a'-b'*q,-t',t'*q-s',d) where+ (a',b',s',t',d) = extended_euclid y r+ (q,r) = divmod x y++-- | Find the inverse of a unit in a Euclidean domain. If the given+-- element is not a unit, return 'Nothing'.+euclid_inverse :: (EuclideanDomain a) => a -> Maybe a+euclid_inverse x+ | x == 0 = Nothing+ | r == 0 = Just q+ | otherwise = Nothing+ where+ (q,r) = divmod 1 x++-- | Determine whether an element of a Euclidean domain is a unit.+is_unit :: (EuclideanDomain a) => a -> Bool+is_unit = isJust . euclid_inverse++-- | Compute the inverse of /a/ in /R/\/(p), where /R/ is a Euclidean+-- domain. Note: this works whenever /a/ and /p/ are relatively+-- prime. If /a/ and /p/ are not relatively prime, return 'Nothing'.+inv_mod :: EuclideanDomain a => a -> a -> Maybe a+inv_mod p a = case euclid_inverse d of+ Just d' -> let (q,r) = (b*d') `divmod` p in Just r+ Nothing -> Nothing+ where+ (b,_,_,_,d) = extended_euclid a p++-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | For /y/ ≠ 0, find the integer /q/ closest to /x/ \/ /y/. This+-- works regardless of whether /x/ and\/or /y/ are positive or+-- negative. The distance /q/ − /x/ \/ /y/ is guaranteed to be in+-- (-1\/2, 1\/2].+rounddiv :: (Integral a) => a -> a -> a+rounddiv x y = + -- Note: the use of "quot" and "div" is crucial for the signs to+ -- work out correctly.+ (x + y `quot` 2) `div` y++infixl 7 `rounddiv`
+ Quantum/Synthesis/EulerAngles.hs view
@@ -0,0 +1,51 @@+-- | This module provides functions for converting between matrices in+-- /U/(2) and their Euler angle representation.++module Quantum.Synthesis.EulerAngles where++import Quantum.Synthesis.Ring+import Quantum.Synthesis.Matrix+import Quantum.Synthesis.ArcTan2++-- ----------------------------------------------------------------------+-- * Documentation++-- | Decompose a unitary operator /U/ into Euler angles (α, β, γ, δ).+-- These angles are computed so that+-- +-- * /U/ = [exp /i/α] R[sub /z/](β) R[sub /x/](γ) R[sub /z/](δ).+euler_angles :: (Floating a, ArcTan2 a) => Matrix Two Two (Cplx a) -> (a, a, a, a)+euler_angles op = (alpha, beta, gamma, delta) where+ ((a, b), (c, d)) = from_matrix2x2 op+ beta_plus_delta_over_2 = phase d - alpha+ beta_minus_delta_over_2 = phase c - alpha + pi/2+ alpha = phase (a*d - b*c) / 2+ gamma = 2 * arctan2 (mag b) (mag a)+ delta = phase (b * d * i * adj (a*d - b*c))+ beta = 2 * phase (d * cis (-alpha - delta/2) + c * cis (-alpha+delta/2) * i)++ mag (Cplx a b) = sqrt (a^2 + b^2)+ phase (Cplx a b) = arctan2 b a++ cis x = Cplx (cos x) (sin x)++ adj (Cplx x y) = Cplx x (-y)++-- | Compute the operator+-- +-- * /U/ = [exp /i/α] R[sub /z/](β) R[sub /x/](γ) R[sub /z/](δ).+-- +-- from the given Euler angles.+matrix_of_euler_angles :: (Floating a) => (a, a, a, a) -> Matrix Two Two (Cplx a) +matrix_of_euler_angles (alpha, beta, gamma, delta) = op where+ op = opa * opb * opc * opd+ opa = cplx_cis alpha `scalarmult` 1+ opb = zrot beta+ opc = hadamard * zrot gamma * hadamard+ opd = zrot delta+ + cplx_cis theta = Cplx (cos theta) (sin theta)+ hadamard = Cplx (sqrt 0.5) 0 `scalarmult` matrix2x2 (1, 1) (1, -1)+ zrot gamma = matrix2x2 (cplx_cis (-gamma/2), 0) (0, cplx_cis (gamma/2))++
+ Quantum/Synthesis/LaTeX.hs view
@@ -0,0 +1,180 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverlappingInstances #-}++-- | This module provides some functionality for pretty-printing+-- certain types to LaTeX format.++module Quantum.Synthesis.LaTeX where++import Quantum.Synthesis.CliffordT+import Quantum.Synthesis.MultiQubitSynthesis+import Quantum.Synthesis.Ring+import Quantum.Synthesis.Matrix+import Quantum.Synthesis.SymReal++import Text.Printf+import Data.Ratio++-- | A type class for things that can be printed to LaTeX format. +-- +-- Minimal complete definition: 'showlatex' or 'showlatex_p'.++-- This is a bit naive at the moment - to do it properly, one should+-- perhaps also supply context information, for example math mode/text+-- mode.+class ShowLaTeX a where+ -- | Print to LaTeX format.+ showlatex :: a -> String+ showlatex x = showlatex_p 0 x ""+ + -- | Print to LaTeX format, with precedence. Analogous to 'showsPrec'.+ showlatex_p :: Int -> a -> ShowS+ showlatex_p _ x s = showlatex x ++ s++instance ShowLaTeX TwoLevel where+ showlatex (TL_X i j) = printf "X\\level{%d,%d} " (i+1) (j+1)+ showlatex (TL_H i j) = printf "H\\level{%d,%d} " (i+1) (j+1)+ showlatex (TL_T m i j)+ | m' == 0 = ""+ | m' == 1 = printf "T\\level{%d,%d} " (i+1) (j+1)+ | otherwise = printf "T^%d\\level{%d,%d} " m' (i+1) (j+1)+ where m' = m `mod` 8+ showlatex (TL_omega m i)+ | m' == 0 = ""+ | m' == 1 = printf "\\omega\\level{%d} " (i+1)+ | otherwise = printf "\\omega^%d\\level{%d} " m' (i+1)+ where m' = m `mod` 8+ +instance ShowLaTeX [TwoLevel] where+ showlatex = concat . map showlatex++instance ShowLaTeX Integer where+ showlatex = show++instance ShowLaTeX ZOmega where+ showlatex (Omega a b c d) = format_signed_list list2 where+ list = map signedunit [(a,"\\omega^3"),(b,"\\omega^2"),(c,"\\omega"),(d,"")]+ list2 = filter (\(s,a) -> s /= 0) list+ signedunit (a, u) + | u == "" = (s, showlatex a')+ | a' == 1 = (s, u)+ | otherwise = (s, showlatex a' ++ u)+ where+ (s,a') = tosigned a+ tosigned a + | a < 0 = (-1,-a)+ | a == 0 = (0,0)+ | otherwise = (1,a)+ format_signed_list [] = "0"+ format_signed_list ((1,a):t) = a ++ cont t + format_signed_list ((_,a):t) = "-" ++ a ++ cont t + cont [] = ""+ cont ((1,a):t) = "+" ++ a ++ cont t+ cont ((0,a):t) = cont t+ cont ((_,a):t) = "-" ++ a ++ cont t++instance (ShowLaTeX a, Nat n) => ShowLaTeX (Matrix n m a) where+ showlatex (Matrix a) = "\\zmatrix{" ++ replicate m 'c' ++ "}{" ++ entries ++ "}" where+ m = length (list_of_vector a)+ entries = concat $ list_of_vector $ vector_map showcolumn (vector_transpose a)+ showcolumn :: ShowLaTeX a => Vector m a -> String+ showcolumn Nil = "\\\\"+ showcolumn (h `Cons` Nil) = showlatex h ++ "\\\\"+ showcolumn (h `Cons` t) = showlatex h ++ " & " ++ showcolumn t++instance ShowLaTeX Rational where+ showlatex r = "\\frac{" ++ showlatex num ++ "}{" ++ showlatex denom ++ "}"+ where+ num = numerator r+ denom = numerator r++instance ShowLaTeX Dyadic where+ showlatex = showlatex . toRational++instance (ShowLaTeX a, Eq a, Ring a) => ShowLaTeX (RootTwo a) where+ showlatex_p d (RootTwo a 0) = showlatex_p d a+ showlatex_p d (RootTwo 0 1) = showString "\\sqrt{2}"+ showlatex_p d (RootTwo 0 (-1)) = showParen (d >= 7) $ showString "-\\sqrt{2}"+ showlatex_p d (RootTwo 0 b) = showParen (d >= 8) $ + showlatex_p 7 b . showString " \\sqrt{2}"+ showlatex_p d (RootTwo a b) | signum b == 1 = showParen (d >= 7) $+ showlatex_p 6 a . showString " + " . showlatex_p 6 (RootTwo 0 b)+ showlatex_p d (RootTwo a b) | otherwise = showParen (d >= 7) $+ showlatex_p 6 a . showString " - " . showlatex_p 7 (RootTwo 0 (-b))+ ++instance ShowLaTeX (Omega Z2) where+ showlatex (Omega a b c d) = concat $ map show [a,b,c,d]++instance (ShowLaTeX a, Ring a, Eq a) => ShowLaTeX (Cplx a) where+ showlatex_p d (Cplx a 0) = showlatex_p d a+ showlatex_p d (Cplx 0 1) = showString "i"+ showlatex_p d (Cplx 0 (-1)) = showParen (d >= 7) $ showString "-i"+ showlatex_p d (Cplx 0 b) = showParen (d >= 8) $+ showlatex_p 7 b . showString "\\,i"+ showlatex_p d (Cplx a b) | signum b == 1 = showParen (d >= 7) $+ showlatex_p 6 a . showString "+" . showlatex_p 7 (Cplx 0 b)+ showlatex_p d (Cplx a b) | otherwise = showParen (d >= 7) $ + showlatex_p 6 a . showString "-" . showlatex_p 7 (Cplx 0 (-b))++instance ShowLaTeX Double where+ showlatex x = printf "%0.10f" x++-- This is an overlapping instance+instance Nat n => ShowLaTeX (Matrix n m DOmega) where+ showlatex = showlatex_denomexp++-- This is an overlapping instance+instance Nat n => ShowLaTeX (Matrix n m DRComplex) where+ showlatex = showlatex_denomexp++-- | Generic showlatex-like method that factors out a common+-- denominator exponent.+showlatex_denomexp :: (WholePart a b, ShowLaTeX b, DenomExp a) => a -> String+showlatex_denomexp a+ | k == 0 = showlatex b+ | k == 1 = "\\frac{1}{\\sqrt{2}}" ++ showlatex b+ | otherwise = "\\frac{1}{\\sqrt{2}^{" ++ show k ++ "}}" ++ showlatex b+ where (b, k) = denomexp_decompose a++instance ShowLaTeX [Gate] where+ showlatex [] = "\\epsilon"+ showlatex gates = aux 0 gates where+ aux n (W:t) = aux (n+1) t+ aux 0 [] = ""+ aux 1 [] = "{\\omega}"+ aux n [] = "\\omega^" ++ show n+ aux 0 (h:t) = show h ++ aux 0 t+ aux n t = aux n [] ++ aux 0 t++instance ShowLaTeX SymReal where+ showlatex_p d (Const x) = showlatex_p d x+ showlatex_p d (Decimal x s) = showString s+ showlatex_p d (Plus x y) = showParen (d > 6) $ showlatex_p 6 x . showString "+" . showlatex_p 6 y+ showlatex_p d (Minus x y) = showParen (d > 6) $ showlatex_p 6 x . showString "-" . showlatex_p 7 y+ showlatex_p d (Times x y) = showParen (d > 7) $ showlatex_p 7 x . showString "\\cdot" . showlatex_p 7 y+ showlatex_p d (Div x y) = showParen (d > 7) $ showlatex_p 7 x . showString "/" . showlatex_p 8 y+ showlatex_p d (Power x y) = showParen (d > 11) $ showlatex_p 12 x . showString "^{" . showlatex_p 0 y . showString "}"+ showlatex_p d (Negate x) = showParen (d > 5) $ showString "-" . showlatex_p 7 x+ showlatex_p d (Abs x) = showParen (d > 10) $ showString "|" . showlatex_p 11 x . showString "|"+ showlatex_p d (Signum x) = showParen (d > 10) $ showString "\\signum " . showlatex_p 11 x+ showlatex_p d (Recip x) = showParen (d > 7) $ showString "1/" . showlatex_p 8 x+ showlatex_p d Pi = showString "\\pi"+ showlatex_p d Euler = showString "e"+ showlatex_p d (Exp x) = showParen (d > 10) $ showString "e^{" . showlatex_p 0 x . showString "}"+ showlatex_p d (Sqrt x) = showString "\\sqrt{" . showlatex_p 0 x . showString "}"+ showlatex_p d (Log x) = showParen (d > 10) $ showString "\\log " . showlatex_p 11 x+ showlatex_p d (Sin x) = showParen (d > 10) $ showString "\\sin " . showlatex_p 11 x+ showlatex_p d (Tan x) = showParen (d > 10) $ showString "\\tan " . showlatex_p 11 x+ showlatex_p d (Cos x) = showParen (d > 10) $ showString "\\cos " . showlatex_p 11 x+ showlatex_p d (ASin x) = showParen (d > 10) $ showString "\\asin " . showlatex_p 11 x+ showlatex_p d (ATan x) = showParen (d > 10) $ showString "\\atan " . showlatex_p 11 x+ showlatex_p d (ACos x) = showParen (d > 10) $ showString "\\acos " . showlatex_p 11 x+ showlatex_p d (Sinh x) = showParen (d > 10) $ showString "\\sinh " . showlatex_p 11 x+ showlatex_p d (Tanh x) = showParen (d > 10) $ showString "\\tanh " . showlatex_p 11 x+ showlatex_p d (Cosh x) = showParen (d > 10) $ showString "\\cosh " . showlatex_p 11 x+ showlatex_p d (ASinh x) = showParen (d > 10) $ showString "\\asinh " . showlatex_p 11 x+ showlatex_p d (ATanh x) = showParen (d > 10) $ showString "\\atanh " . showlatex_p 11 x+ showlatex_p d (ACosh x) = showParen (d > 10) $ showString "\\acosh " . showlatex_p 11 x+ showlatex_p d (ArcTan2 y x) = showParen (d > 10) $ showString "\\arctan2 " . showlatex_p 11 y . showString " " . showlatex_p 11 x
+ Quantum/Synthesis/Matrix.hs view
@@ -0,0 +1,601 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverlappingInstances #-}+{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE EmptyDataDecls #-}++{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}++-- | This module provides fixed but arbitrary sized vectors and+-- matrices. The dimensions of the vectors and matrices are determined+-- by the type, for example,+-- +-- > Matrix Two Three Complex+-- +-- for complex 2×3-matrices. The type system ensures that there are no+-- run-time dimension errors.++module Quantum.Synthesis.Matrix where++import Quantum.Synthesis.Ring++-- ----------------------------------------------------------------------+-- * Type-level natural numbers+ +-- $ Note: with Haskell 7.4.2 data-kinds, this could be replaced by a+-- tighter definition; however, the following works just fine in+-- Haskell 7.2.++-- | Type-level representation of zero.+data Zero++-- | Type-level representation of successor.+data Succ a++-- | The natural number 1 as a type.+type One = Succ Zero++-- | The natural number 2 as a type.+type Two = Succ One++-- | The natural number 3 as a type.+type Three = Succ Two++-- | The natural number 4 as a type.+type Four = Succ Three++-- | The natural number 5 as a type.+type Five = Succ Four++-- | The natural number 6 as a type.+type Six = Succ Five++-- | The natural number 7 as a type.+type Seven = Succ Six++-- | The natural number 8 as a type.+type Eight = Succ Seven++-- | The natural number 9 as a type.+type Nine = Succ Eight++-- | The natural number 10 as a type.+type Ten = Succ Nine++-- | The 10th successor of a natural number type. For example, the+-- natural number 18 as a type is+-- +-- > Ten_and Eight+type Ten_and a = Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ a)))))))))++-- | A data type for the natural numbers. Specifically, if /n/ is a+-- type-level natural number, then+-- +-- > NNat n+-- +-- is a singleton type containing only the natural number /n/.+data NNat :: * -> * where+ Zero :: NNat Zero+ Succ :: (Nat n) => NNat n -> NNat (Succ n)++-- | Convert an 'NNat' to an 'Integer'.+fromNNat :: NNat n -> Integer+fromNNat Zero = 0+fromNNat (Succ n) = 1 + fromNNat n++instance Show (NNat n) where+ show = show . fromNNat++-- | A type class for the natural numbers. The members are exactly the+-- type-level natural numbers.+class Nat n where+ -- | Return a term-level natural number corresponding to this+ -- type-level natural number.+ nnat :: NNat n+ + -- | Return a term-level integer corresponding to this type-level+ -- natural number. The argument is just a dummy argument and is not+ -- evaluated.+ nat :: n -> Integer+ +instance Nat Zero where+ nnat = Zero+ nat n = 0+instance (Nat a) => Nat (Succ a) where+ nnat = Succ nnat+ nat n = 1 + nat (un n) where+ un :: Succ a -> a+ un = undefined++-- | Addition of type-level natural numbers.+type family Plus n m+type instance Zero `Plus` m = m+type instance (Succ n) `Plus` m = Succ (n `Plus` m)++-- | Multiplication of type-level natural numbers.+type family Times n m+type instance Zero `Times` m = Zero+type instance (Succ n) `Times` m = m `Plus` (n `Times` m)++-- ----------------------------------------------------------------------+-- * Fixed-length vectors++-- | @Vector /n/ /a/@ is the type of lists of length /n/ with elements+-- from /a/. We call this a \"vector\" rather than a tuple or list for+-- two reasons: the vectors are homogeneous (all elements have the+-- same type), and they are strict: if any one component is undefined,+-- the whole vector is undefined.+data Vector :: * -> * -> * where+ Nil :: Vector Zero a+ Cons :: !a -> !(Vector n a) -> Vector (Succ n) a++infixr 5 `Cons`++instance (Eq a) => Eq (Vector n a) where+ Nil == Nil = True+ Cons a as == Cons b bs = a == b && as == bs++instance (Show a) => Show (Vector n a) where+ showsPrec d x = showParen (d >= 11) $ showString ("vector " ++ show (list_of_vector x))++instance (ToDyadic a b) => ToDyadic (Vector n a) (Vector n b) where+ maybe_dyadic as = vector_sequence (vector_map maybe_dyadic as)++instance (WholePart a b) => WholePart (Vector n a) (Vector n b) where + from_whole = vector_map from_whole+ to_whole = vector_map to_whole+ +instance (DenomExp a) => DenomExp (Vector n a) where+ denomexp as = denomexp (list_of_vector as)+ denomexp_factor as k = vector_map (\a -> denomexp_factor a k) as+ +-- | Construct a vector of length 1.+vector_singleton :: a -> Vector One a+vector_singleton x = x `Cons` Nil++-- | Return the length of a vector. Since this information is+-- contained in the type, the vector argument is never evaluated and+-- can be a dummy (undefined) argument.+vector_length :: (Nat n) => Vector n a -> Integer+vector_length = nat . un where+ un :: Vector n a -> n+ un = undefined++-- | Convert a fixed-length list to an ordinary list.+list_of_vector :: Vector n a -> [a]+list_of_vector Nil = []+list_of_vector (Cons h t) = h : list_of_vector t++-- | Zip two equal length lists.+vector_zipwith :: (a -> b -> c) -> Vector n a -> Vector n b -> Vector n c+vector_zipwith f Nil Nil = Nil+vector_zipwith f (Cons a as) (Cons b bs) = Cons c cs where+ c = f a b+ cs = vector_zipwith f as bs++-- | Map a function over a fixed-length list.+vector_map :: (a -> b) -> Vector n a -> Vector n b+vector_map f Nil = Nil+vector_map f (Cons a as) = Cons (f a) (vector_map f as)++-- | Create the vector (0, 1, …, /n/-1).+vector_enum :: (Num a, Nat n) => Vector n a+vector_enum = aux nnat 0 where+ aux :: (Num a) => NNat n -> a -> Vector n a+ aux Zero a = Nil+ aux (Succ n) a = Cons a (aux n (a+1))++-- | Create the vector (/f/(0), /f/(1), …, /f/(/n/-1)).+vector_of_function :: (Num a, Nat n) => (a -> b) -> Vector n b+vector_of_function f = vector_map f vector_enum++-- | Construct a vector from a list. Note: since the length of the+-- vector is a type-level integer, it cannot be inferred from the+-- length of the input list; instead, it must be specified explicitly+-- in the type. It is an error to apply this function to a list of+-- the wrong length.+vector :: (Nat n) => [a] -> Vector n a+vector = aux nnat where+ aux :: NNat n -> [a] -> Vector n a+ aux Zero [] = Nil+ aux (Succ n) (h:t) = Cons h (aux n t)+ aux _ _ = error "vector: length mismatch"++-- | Return the /i/th element of the vector. Counting starts from 0.+-- Throws an error if the index is out of range.+vector_index :: (Integral i) => Vector n a -> i -> a+vector_index v i = list_of_vector v !! fromIntegral i++-- | Return a fixed-length list consisting of a repetition of the+-- given element. Unlike 'replicate', no count is needed, because this+-- information is already contained in the type. However, the type+-- must of course be inferable from the context.+vector_repeat :: (Nat n) => a -> Vector n a+vector_repeat x = vector_of_function (const x)++-- | Turn a list of columns into a list of rows.+vector_transpose :: (Nat m) => Vector n (Vector m a) -> Vector m (Vector n a)+vector_transpose Nil = vector_repeat Nil+vector_transpose (Cons a as) = vector_zipwith Cons a (vector_transpose as)++-- | Left strict fold over a fixed-length list.+vector_foldl :: (a -> b -> a) -> a -> Vector n b -> a+vector_foldl f x l = foldl f x (list_of_vector l)++-- | Right fold over a fixed-length list.+vector_foldr :: (a -> b -> b) -> b -> Vector n a -> b+vector_foldr f x l = foldr f x (list_of_vector l)++-- | Return the tail of a fixed-length list. Note that the type system+-- ensures that this never fails.+vector_tail :: Vector (Succ n) a -> Vector n a+vector_tail (Cons h t) = t++-- | Return the head of a fixed-length list. Note that the type system+-- ensures that this never fails.+vector_head :: Vector (Succ n) a -> a+vector_head (Cons h t) = h++-- | Append two fixed-length lists.+vector_append :: Vector n a -> Vector m a -> Vector (n `Plus` m) a+vector_append Nil v = v+vector_append (Cons h t) v = Cons h (vector_append t v)++-- | Version of 'sequence' for fixed-length lists.+vector_sequence :: (Monad m) => Vector n (m a) -> m (Vector n a)+vector_sequence Nil = return Nil+vector_sequence (Cons a as) = do+ a' <- a+ as' <- vector_sequence as+ return (Cons a' as')++-- ----------------------------------------------------------------------+-- * Matrices++-- | An /m/×/n/-matrix is a list of /n/ columns, each of which is a+-- list of /m/ scalars. The type of square matrices of any fixed+-- dimension is an instance of the 'Ring' class, and therefore the+-- usual symbols, such as \"'+'\" and \"'*'\" can be used on+-- them. However, the non-square matrices, the symbols \"'.+.'\" and+-- \"'.*.'\" must be used.+data Matrix m n a = Matrix !(Vector n (Vector m a))+ deriving (Eq)++instance (Nat m, Show a) => Show (Matrix m n a) where+ showsPrec d m = showParen (d >= 11) $ showString ("matrix " ++ show (rows_of_matrix m))+ +-- This is an overlapping instance.+instance (Nat m) => Show (Matrix m n DRootTwo) where+ showsPrec = showsPrec_DenomExp+ +-- This is an overlapping instance.+instance (Nat m) => Show (Matrix m n DRComplex) where+ showsPrec = showsPrec_DenomExp++-- This is an overlapping instance.+instance (Nat m) => Show (Matrix m n DOmega) where+ showsPrec = showsPrec_DenomExp+ +instance (ToDyadic a b) => ToDyadic (Matrix m n a) (Matrix m n b) where+ maybe_dyadic (Matrix a) = do+ b <- maybe_dyadic a+ return (Matrix b)++instance (WholePart a b) => WholePart (Matrix m n a) (Matrix m n b) where+ from_whole (Matrix m) = Matrix (from_whole m)+ to_whole (Matrix m) = Matrix (to_whole m)++instance (DenomExp a) => DenomExp (Matrix m n a) where+ denomexp (Matrix m) = denomexp m+ denomexp_factor (Matrix m) k = Matrix (denomexp_factor m k)++-- | Decompose a matrix into a list of columns.+unMatrix :: Matrix m n a -> (Vector n (Vector m a))+unMatrix (Matrix m) = m++-- | Return the size (/m/, /n/) of a matrix, where /m/ is the number+-- of rows, and /n/ is the number of columns. Since this information+-- is contained in the type, the matrix argument is not evaluated and+-- can be a dummy (undefined) argument.+matrix_size :: (Nat m, Nat n) => Matrix m n a -> (Integer, Integer)+matrix_size op = (nat (m op), nat (n op)) where+ m :: Matrix m n a -> m+ m = undefined+ n :: Matrix m n a -> n+ n = undefined++-- ----------------------------------------------------------------------+-- ** Basic matrix operations++-- | Addition of /m/×/n/-matrices. We use a special symbol because+-- /m/×/n/-matrices do not form a ring; only /n/×/n/-matrices form a+-- ring (in which case the normal symbol \"'+'\" also works).+(.+.) :: (Num a) => Matrix m n a -> Matrix m n a -> Matrix m n a+Matrix a .+. Matrix b = Matrix c where+ c = vector_zipwith (vector_zipwith (+)) a b++infixl 6 .+.++-- | Subtraction of /m/×/n/-matrices. We use a special symbol because+-- /m/×/n/-matrices do not form a ring; only /n/×/n/-matrices form a+-- ring (in which case the normal symbol \"'-'\" also works).+(.-.) :: (Num a) => Matrix m n a -> Matrix m n a -> Matrix m n a+Matrix a .-. Matrix b = Matrix c where+ c = vector_zipwith (vector_zipwith (-)) a b++infixl 6 .-.++-- | Map some function over every element of a matrix.+matrix_map :: (a -> b) -> Matrix m n a -> Matrix m n b+matrix_map f (Matrix a) = Matrix b where+ b = vector_map (vector_map f) a++-- | Create the matrix whose /i/,/j/-entry is (/i/,/j/). Here /i/ and+-- /j/ are 0-based, i.e., the top left entry is (0,0).+matrix_enum :: (Num a, Nat n, Nat m) => Matrix m n (a,a)+matrix_enum = Matrix (vector_of_function f) where+ f i = vector_of_function (\j -> (j,i))++-- | Create the matrix whose /i/,/j/-entry is @f i j@. Here /i/ and+-- /j/ are 0-based, i.e., the top left entry is @f 0 0@.+matrix_of_function :: (Num a, Nat n, Nat m) => (a -> a -> b) -> Matrix m n b+matrix_of_function f = matrix_map (uncurry f) matrix_enum++-- | Multiplication of a scalar and an /m/×/n/-matrix.+scalarmult :: (Num a) => a -> Matrix m n a -> Matrix m n a+scalarmult x m = matrix_map (x *) m++infixl 7 `scalarmult`++-- | Multiplication of /m/×/n/-matrices. We use a special symbol+-- because /m/×/n/-matrices do not form a ring; only /n/×/n/-matrices+-- form a ring (in which case the normal symbol \"'*'\" also works).+(.*.) :: (Num a, Nat m) => Matrix m n a -> Matrix n p a -> Matrix m p a+Matrix a .*. Matrix b = Matrix c where+ c = vector_map (a `mmv`) b+ + mmv :: (Num a, Nat m) => Vector n (Vector m a) -> Vector n a -> Vector m a+ Nil `mmv` Nil = vector_repeat 0+ (Cons h Nil) `mmv` (Cons k Nil) = k `msv` h+ (Cons h t) `mmv` (Cons k s) = (k `msv` h) `avv` (t `mmv` s)+ + msv :: (Num b) => b -> Vector n b -> Vector n b+ k `msv` h = vector_map (k*) h+ + avv :: (Num c) => Vector n c -> Vector n c -> Vector n c+ v `avv` w = vector_zipwith (+) v w++infixl 7 .*.++-- | Return the 0 matrix of the given dimension.+null_matrix :: (Num a, Nat n, Nat m) => Matrix m n a+null_matrix = Matrix (vector_repeat (vector_repeat 0))++-- | Take the transpose of an /m/×/n/-matrix.+matrix_transpose :: (Nat m) => Matrix m n a -> Matrix n m a+matrix_transpose (Matrix a) = Matrix b where+ b = vector_transpose a++-- | Take the adjoint of an /m/×/n/-matrix. Unlike 'adj', this can be+-- applied to non-square matrices.+adjoint :: (Nat m, Adjoint a) => Matrix m n a -> Matrix n m a+adjoint (Matrix a) = Matrix c where+ b = vector_map (vector_map adj) a+ c = vector_transpose b+ +-- | Return the element in the /i/th row and /j/th column of the+-- matrix. Counting of rows and columns starts from 0. Throws an error+-- if the index is out of range.+matrix_index :: (Integral i) => Matrix m n a -> i -> i -> a+matrix_index (Matrix a) i j = a `vector_index` j `vector_index` i++-- | Return a list of all the entries of a matrix, in some fixed but+-- unspecified order.+matrix_entries :: Matrix m n a -> [a]+matrix_entries (Matrix m) = + concat $ map list_of_vector $ list_of_vector m++-- | Version of 'sequence' for matrices.+matrix_sequence :: (Monad m) => Matrix n p (m a) -> m (Matrix n p a)+matrix_sequence (Matrix m) = do+ m' <- vector_sequence (vector_map vector_sequence m)+ return (Matrix m')++-- | Return the trace of a square matrix.+tr :: (Ring a) => Matrix n n a -> a+tr (Matrix a) = aux a where+ aux :: (Num a) => Vector n (Vector n a) -> a+ aux Nil = 0+ aux ((h `Cons` t) `Cons` s) = h + aux (vector_map vector_tail s)++-- | Return the square of the Hilbert-Schmidt norm of an+-- /m/×/n/-matrix, defined by ‖/M/‖² = tr /M/[sup †]/M/.+hs_sqnorm :: (Ring a, Adjoint a, Nat n) => Matrix n m a -> a+hs_sqnorm m = tr (m .*. adjoint m)++-- ----------------------------------------------------------------------+-- Class instances for the ring of square matrices++instance (Num a, Nat n) => Num (Matrix n n a) where+ (+) = (.+.)+ (*) = (.*.)+ negate = scalarmult (-1)+ (-) = (.-.)+ fromInteger x = matrix_of_function (\i j -> if i == j then fromInteger x else 0)+ abs a = a+ signum a = 1+ +instance (Nat n, Adjoint a) => Adjoint (Matrix n n a) where+ adj (Matrix a) = Matrix c where+ b = vector_map (vector_map adj) a+ c = vector_transpose b++instance (Nat n, Adjoint2 a) => Adjoint2 (Matrix n n a) where+ adj2 (Matrix a) = Matrix b where+ b = vector_map (vector_map adj2) a++instance (HalfRing a, Nat n) => HalfRing (Matrix n n a) where+ half = scalarmult half 1++instance (RootHalfRing a, Nat n) => RootHalfRing (Matrix n n a) where+ roothalf = scalarmult roothalf 1++instance (RootTwoRing a, Nat n) => RootTwoRing (Matrix n n a) where+ roottwo = scalarmult roottwo 1++instance (ComplexRing a, Nat n) => ComplexRing (Matrix n n a) where+ i = scalarmult i 1++-- ----------------------------------------------------------------------+-- ** Operations on block matrices++-- | Stack matrices vertically.+stack_vertical :: Matrix m n a -> Matrix p n a -> Matrix (m `Plus` p) n a+stack_vertical (Matrix a) (Matrix b) = (Matrix c) where+ c = vector_zipwith vector_append a b++-- | Stack matrices horizontally.+stack_horizontal :: Matrix m n a -> Matrix m p a -> Matrix m (n `Plus` p) a+stack_horizontal (Matrix a) (Matrix b) = (Matrix c) where+ c = vector_append a b+ +-- | Repeat a matrix vertically, according to some vector of scalars.+tensor_vertical :: (Num a, Nat n) => Vector p a -> Matrix m n a -> Matrix (p `Times` m) n a+tensor_vertical v m = concat_vertical (vector_map (`scalarmult` m) v)+ +-- | Vertically concatenate a vector of matrices.+concat_vertical :: (Num a, Nat n) => Vector p (Matrix m n a) -> Matrix (p `Times` m) n a+concat_vertical Nil = null_matrix+concat_vertical (Cons h t) = stack_vertical h (concat_vertical t)++-- | Repeat a matrix horizontally, according to some vector of scalars.+tensor_horizontal :: (Num a, Nat m) => Vector p a -> Matrix m n a -> Matrix m (p `Times` n) a+tensor_horizontal v m = concat_horizontal (vector_map (`scalarmult` m) v)+ +-- | Horizontally concatenate a vector of matrices.+concat_horizontal :: (Num a, Nat m) => Vector p (Matrix m n a) -> Matrix m (p `Times` n) a+concat_horizontal Nil = null_matrix+concat_horizontal (Cons h t) = stack_horizontal h (concat_horizontal t)++-- | Kronecker tensor of two matrices.+tensor :: (Num a, Nat n, Nat (p `Times` m)) => Matrix p q a -> Matrix m n a -> Matrix (p `Times` m) (q `Times` n) a+tensor a b = ab3 where+ Matrix ab1 = matrix_map (`scalarmult` b) a+ ab2 = vector_map concat_vertical ab1+ ab3 = concat_horizontal ab2++-- | Form a diagonal block matrix.+oplus :: (Num a, Nat m, Nat q, Nat n, Nat p) => Matrix p q a -> Matrix m n a -> Matrix (p `Plus` m) (q `Plus` n) a+oplus (a :: Matrix p q a) (b :: Matrix m n a) = + (a `stack_vertical` (null_matrix :: Matrix m q a)) `stack_horizontal` ((null_matrix :: Matrix p n a) `stack_vertical` b)++-- | Form a controlled gate.+matrix_controlled :: (Eq a, Num a, Nat n) => Matrix n n a -> Matrix (n `Plus` n) (n `Plus` n) a+matrix_controlled (m :: Matrix n n a) = oplus (1 :: Matrix n n a) m++-- ----------------------------------------------------------------------+-- ** Constructors and destructors++-- | A convenient abbreviation for the type of 2×2-matrices.+type U2 a = Matrix Two Two a++-- | A convenient abbreviation for the type of 3×3-matrices.+type SO3 a = Matrix Three Three a++-- | A convenience constructor for matrices: turn a list of columns+-- into a matrix. +-- +-- Note: since the dimensions of the matrix are type-level integers,+-- they cannot be inferred from the dimensions of the input; instead,+-- they must be specified explicitly in the type. It is an error to+-- apply this function to a list of the wrong dimension.+matrix_of_columns :: (Nat n, Nat m) => [[a]] -> Matrix n m a+matrix_of_columns columns = Matrix m where+ m = vector $ map vector columns++-- | A convenience constructor for matrices: turn a list of rows into+-- a matrix.+-- +-- Note: since the dimensions of the matrix are type-level integers,+-- they cannot be inferred from the dimensions of the input; instead,+-- they must be specified explicitly in the type. It is an error to+-- apply this function to a list of the wrong dimension.+matrix_of_rows :: (Nat n, Nat m) => [[a]] -> Matrix n m a+matrix_of_rows = matrix_transpose . matrix_of_columns++-- | A synonym for 'matrix_of_rows'.+matrix :: (Nat n, Nat m) => [[a]] -> Matrix n m a+matrix = matrix_of_rows++-- | Turn a matrix into a list of columns.+columns_of_matrix :: Matrix n m a -> [[a]]+columns_of_matrix (Matrix m) = + map list_of_vector (list_of_vector m)++-- | Turn a matrix into a list of rows.+rows_of_matrix :: (Nat n) => Matrix n m a -> [[a]]+rows_of_matrix = columns_of_matrix . matrix_transpose++-- | A convenience constructor for 2×2-matrices. The arguments are by+-- rows.+matrix2x2 :: (a, a) -> (a, a) -> Matrix Two Two a+matrix2x2 (a, b) (c, d) = matrix_of_columns [[a,c], [b,d]]++-- | A convenience destructor for 2×2-matrices. The result is by rows.+from_matrix2x2 :: Matrix Two Two a -> ((a, a), (a, a))+from_matrix2x2 (Matrix ((a `Cons` c `Cons` Nil) `Cons` (b `Cons` d `Cons` Nil) `Cons` Nil)) = ((a, b), (c, d)) ++-- | A convenience constructor for 3×3-matrices. The arguments are by+-- rows.+matrix3x3 :: (a, a, a) -> (a, a, a) -> (a, a, a) -> Matrix Three Three a+matrix3x3 (a0, a1, a2) (b0, b1, b2) (c0, c1, c2) = + matrix_of_columns [[a0, b0, c0], [a1, b1, c1], [a2, b2, c2]]++-- | A convenience constructor for 4×4-matrices. The arguments are by+-- rows.+matrix4x4 :: (a, a, a, a) -> (a, a, a, a) -> (a, a, a, a) -> (a, a, a, a) -> Matrix Four Four a+matrix4x4 (a0, a1, a2, a3) (b0, b1, b2, b3) (c0, c1, c2, c3) (d0, d1, d2, d3) = + matrix_of_columns [[a0, b0, c0, d0], [a1, b1, c1, d1], [a2, b2, c2, d2], [a3, b3, c3, d3]]++-- | A convenience constructor for 3-dimensional column vectors.+column3 :: (a, a, a) -> Matrix Three One a+column3 (a, b, c) = matrix_of_columns [[a, b, c]]++-- | A convenience destructor for 3-dimensional column vectors. This+-- is the inverse of 'column3'.+from_column3 :: Matrix Three One a -> (a, a, a)+from_column3 (Matrix ((a `Cons` b `Cons` c `Cons` Nil) `Cons` Nil)) = (a, b, c)++-- | A convenience constructor for turning a vector into a column matrix.+column_matrix :: Vector n a -> Matrix n One a+column_matrix v = Matrix (vector_singleton v)++-- ----------------------------------------------------------------------+-- ** Particular matrices++-- | Controlled-not gate.+cnot :: (Num a) => Matrix Four Four a+cnot = matrix4x4 (1,0,0,0)+ (0,1,0,0)+ (0,0,0,1)+ (0,0,1,0)++-- | Swap gate.+swap :: (Num a) => Matrix Four Four a+swap = matrix4x4 (1,0,0,0)+ (0,0,1,0)+ (0,1,0,0)+ (0,0,0,1)++-- | A /z/-rotation gate, /R/[sub /z/](θ) = [exp −/i/θ/Z/\/2].+zrot :: (Eq r, Floating r, Adjoint r) => r -> Matrix Two Two (Cplx r)+zrot theta = matrix2x2 (u, 0)+ (0, adj u)+ where+ u = Cplx (cos (theta/2)) (-sin (theta/2))
+ Quantum/Synthesis/MultiQubitSynthesis.hs view
@@ -0,0 +1,468 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE GADTs #-}++-- | This module provides functions for the representation and exact+-- synthesis of multi-qubit Clifford+/T/ operators. +-- +-- The multi-qubit Clifford+/T/ exact synthesis algorithm is described+-- in the paper:+-- +-- * Brett Giles, Peter Selinger. Exact synthesis of multiqubit Clifford+T+-- circuits. /Physical Review A/ 87, 032332 (7 pages), 2013. Available+-- from <http://arxiv.org/abs/1212.0506>.+-- +-- It generalizes the single-qubit exact synthesis algorithm of+-- Kliuchnikov, Maslov, and Mosca.++module Quantum.Synthesis.MultiQubitSynthesis where++import Quantum.Synthesis.Matrix+import Quantum.Synthesis.Ring+import Data.List++-- ----------------------------------------------------------------------+-- * Residues++-- | A type class for things that have residues. In a typical+-- instance, /a/ is a ring whose elements are expressed with+-- coefficients in ℤ, and /b/ is a corresponding ring whose elements+-- are expressed with coefficients in ℤ[sub 2].+class Residue a b | a -> b where+ -- | Return the residue of something.+ residue :: a -> b+ +instance Residue Integer Z2 where+ residue = parity+ +instance Residue a b => Residue (Omega a) (Omega b) where+ residue (Omega a b c d) = Omega (residue a) (residue b) (residue c) (residue d)++instance Residue a b => Residue (RootTwo a) (RootTwo b) where+ residue (RootTwo a b) = RootTwo (residue a) (residue b)+ +instance (Residue a a', Residue b b') => Residue (a,b) (a',b') where+ residue (x,y) = (residue x, residue y)+ +instance Residue () () where + residue = const ()+ +instance (Residue a b) => Residue [a] [b] where + residue = map residue+ +instance (Residue a b) => Residue (Cplx a) (Cplx b) where + residue (Cplx a b) = Cplx (residue a) (residue b)+ +instance (Residue a b) => Residue (Vector n a) (Vector n b) where + residue = vector_map residue+ +instance (Residue a b) => Residue (Matrix m n a) (Matrix m n b) where+ residue (Matrix m) = Matrix (residue m)+ +-- ----------------------------------------------------------------------+-- * One- and two-level operators+ +-- ---------------------------------------------------------------------- +-- ** Symbolic representation++-- | An index for a row or column of a matrix.+type Index = Int++-- | Symbolic representation of one- and two-level operators. Note+-- that the power /k/ in the 'TL_T' and 'TL_omega' constructors can be+-- positive or negative, and should be regarded modulo 8.+-- +-- Note: when we use a list of 'TwoLevel' operators to express a+-- sequence of operators, the operators are meant to be applied+-- right-to-left, i.e., as in the mathematical notation for matrix+-- multiplication. This is the opposite of the quantum circuit+-- notation.+data TwoLevel = + TL_X Index Index -- ^ /X/[sub /i/,/j/].+ | TL_H Index Index -- ^ /H/[sub /i/,/j/].+ | TL_T Int Index Index -- ^ (/T/[sub /i/,/j/])[super /k/].+ | TL_omega Int Index -- ^ (ω[sub /i/])[super /k/].+ deriving (Show, Eq)++-- | Invert a 'TwoLevel' operator.+invert_twolevel :: TwoLevel -> TwoLevel+invert_twolevel (TL_X i j) = TL_X i j+invert_twolevel (TL_H i j) = TL_H i j+invert_twolevel (TL_T m i j) = TL_T (-m) i j+invert_twolevel (TL_omega m j) = TL_omega (-m) j++-- | Invert a list of 'TwoLevel' operators.+invert_twolevels :: [TwoLevel] -> [TwoLevel]+invert_twolevels = reverse . map invert_twolevel++-- ----------------------------------------------------------------------+-- ** Constructors for two-level matrices++-- | Construct a two-level matrix with the given entries.+twolevel_matrix :: (Ring a, Nat n) => (a,a) -> (a,a) -> Index -> Index -> Matrix n n a+twolevel_matrix (a,b) (c,d) i j = matrix_of_function f where+ f x y + | x == i && y == i = a+ | x == i && y == j = b+ | x == j && y == i = c+ | x == j && y == j = d+ | x == y = 1+ | otherwise = 0++-- | Construct a one-level matrix with the given entry.+onelevel_matrix :: (Ring a, Nat n) => a -> Index -> Matrix n n a+onelevel_matrix a i = matrix_of_function f where+ f x y+ | x == i && y == i = a+ | x == y = 1+ | otherwise = 0++-- | Convert a symbolic one- or two-level operator into a matrix.+matrix_of_twolevel :: (ComplexRing a, RootHalfRing a, Nat n) => TwoLevel -> Matrix n n a+matrix_of_twolevel (TL_X i j) = twolevel_matrix (0,1) (1,0) i j+matrix_of_twolevel (TL_H i j) = twolevel_matrix (s,s) (s,-s) i j+ where s = roothalf+matrix_of_twolevel (TL_T k i j) = twolevel_matrix (1,0) (0,omega^(k `mod` 8)) i j+matrix_of_twolevel (TL_omega k i) = onelevel_matrix (omega^(k `mod` 8)) i++-- | Convert a list of symbolic one- or two-level operators into a+-- matrix. Note that the operators are to be applied right-to-left,+-- exactly as in mathematical notation.+matrix_of_twolevels :: (ComplexRing a, RootHalfRing a, Nat n) => [TwoLevel] -> Matrix n n a+matrix_of_twolevels gs = foldl' (*) 1 [ matrix_of_twolevel g | g <- gs ]++-- ----------------------------------------------------------------------+-- * Auxiliary list functions++-- | Replace the /i/th element of a list by /x/.+list_insert :: Index -> a -> [a] -> [a]+list_insert 0 x (h:t) = x:t+list_insert n x (h:t) = h:(list_insert (n-1) x t)+list_insert n x [] = []++-- | Apply a unary operator to element /i/ of a list.+transform_at :: (a -> a) -> Index -> [a] -> [a]+transform_at op i lst = lst' where+ x = lst !! i+ x' = op x+ lst' = list_insert i x' lst++-- | Apply a binary operator to elements /i/ and /j/ of a list.+transform_at2 :: ((a,a) -> (a,a)) -> Index -> Index -> [a] -> [a]+transform_at2 op i j lst = lst' where+ (x,y) = (lst !! i, lst !! j)+ (x',y') = op (x,y)+ lst' = list_insert i x' (list_insert j y' lst)++-- | Split a list into pairs. Return a list of pairs, and a final+-- element if the length of the list was odd.+list_pairs :: [a] -> ([(a,a)], Maybe a)+list_pairs [] = ([], Nothing)+list_pairs [h] = ([], Just h)+list_pairs (h:k:t) = ((h,k):t',r') where (t',r') = list_pairs t++-- ----------------------------------------------------------------------+-- * Functions on ℤ[ω]++-- | Given an element of the form ω[sup /m/], return /m/ ∈ {0,…,7}, or+-- 'Nothing' if not of that form.+log_omega :: ZOmega -> Maybe Int+log_omega (Omega 0 0 0 1) = Just 0+log_omega (Omega 0 0 1 0) = Just 1+log_omega (Omega 0 1 0 0) = Just 2+log_omega (Omega 1 0 0 0) = Just 3+log_omega (Omega 0 0 0 (-1)) = Just 4+log_omega (Omega 0 0 (-1) 0) = Just 5+log_omega (Omega 0 (-1) 0 0) = Just 6+log_omega (Omega (-1) 0 0 0) = Just 7+log_omega _ = Nothing++-- | Multiply a scalar by ω[sup /n/].+omega_power :: (OmegaRing a) => Int -> a -> a+omega_power n x = x * omega^(n `mod` 8)++-- | Divide an element of 'ZOmega' by √2, or throw an error if it is+-- not divisible.+reduce_ZOmega :: ZOmega -> ZOmega+reduce_ZOmega (Omega a b c d) + | even (a-c) && even (b-d) = Omega a' b' c' d'+ | otherwise = error "reduce_ZOmega: element not reducible"+ where+ a' = (b-d) `div` 2+ b' = (c+a) `div` 2+ c' = (b+d) `div` 2+ d' = (c-a) `div` 2++-- | Apply the /X/ operator to a 2-dimensional vector over 'ZOmega'.+opX_zomega :: (ZOmega, ZOmega) -> (ZOmega, ZOmega)+opX_zomega (x,y) = (y,x)++-- | Apply the /H/ operator to a 2-dimensional vector over+-- 'ZOmega'. This throws an error if the result is not well-defined+-- over 'ZOmega'.+opH_zomega :: (ZOmega, ZOmega) -> (ZOmega, ZOmega)+opH_zomega (x,y) = (reduce_ZOmega (x+y), reduce_ZOmega (x-y))++-- | Apply a 'TwoLevel' operator to a 'ZOmega'-vector, represented as+-- a list. Throws an error if any operation produces a scalar that is+-- not in 'ZOmega'.+apply_twolevel_zomega :: TwoLevel -> [ZOmega] -> [ZOmega]+apply_twolevel_zomega (TL_X i j) w = transform_at2 opX_zomega i j w+apply_twolevel_zomega (TL_H i j) w = transform_at2 opH_zomega i j w+apply_twolevel_zomega (TL_T k i j) w = transform_at (omega_power k) j w+apply_twolevel_zomega (TL_omega k i) w = transform_at (omega_power k) i w++-- | Apply a list of 'TwoLevel' operators to a 'ZOmega'-vector,+-- represented as a list. Throws an error if any operation produces a+-- scalar that is not in 'ZOmega'.+apply_twolevels_zomega :: [TwoLevel] -> [ZOmega] -> [ZOmega]+apply_twolevels_zomega gs w = foldr apply_twolevel_zomega' w gs+ where apply_twolevel_zomega' g w = apply_twolevel_zomega g w++-- ----------------------------------------------------------------------+-- * Functions on residues++-- | The /residue type/ of /t/ ∈ ℤ[ω] is the residue of /t/[sup †]/t/.+-- It is 0000, 0001, or 1010.+data ResidueType = RT_0000 | RT_0001 | RT_1010+ deriving (Eq, Ord)++-- | Return the residue's 'ResidueType'.+residue_type :: Omega Z2 -> ResidueType+residue_type r = t where+ (t, _) = residue_type_shift r+ +-- | Return the residue's /shift/.+-- +-- The shift is defined so that: +-- +-- * 0001, 1110, 0011 have shift 0,+-- +-- * 0010, 1101, 0110 have shift 1,+-- +-- * 0100, 1011, 1100 have shift 2, and+-- +-- * 1000, 0111, 1001 have shift 3.+-- +-- Residues of type 'RT_0000' have shift 0.+residue_shift :: Omega Z2 -> Int+residue_shift r = s where+ (_, s) = residue_type_shift r++-- | Return the residue's 'ResidueType' and the shift.+residue_type_shift :: Omega Z2 -> (ResidueType, Int)+residue_type_shift (Omega 0 0 0 0) = (RT_0000, 0)+residue_type_shift (Omega 0 0 0 1) = (RT_0001, 0)+residue_type_shift (Omega 0 0 1 0) = (RT_0001, 1)+residue_type_shift (Omega 0 0 1 1) = (RT_1010, 0)+residue_type_shift (Omega 0 1 0 0) = (RT_0001, 2)+residue_type_shift (Omega 0 1 0 1) = (RT_0000, 0)+residue_type_shift (Omega 0 1 1 0) = (RT_1010, 1)+residue_type_shift (Omega 0 1 1 1) = (RT_0001, 3)+residue_type_shift (Omega 1 0 0 0) = (RT_0001, 3)+residue_type_shift (Omega 1 0 0 1) = (RT_1010, 3)+residue_type_shift (Omega 1 0 1 0) = (RT_0000, 0)+residue_type_shift (Omega 1 0 1 1) = (RT_0001, 2)+residue_type_shift (Omega 1 1 0 0) = (RT_1010, 2)+residue_type_shift (Omega 1 1 0 1) = (RT_0001, 1)+residue_type_shift (Omega 1 1 1 0) = (RT_0001, 0)+residue_type_shift (Omega 1 1 1 1) = (RT_0000, 0)+residue_type_shift _ = undefined -- to turn off a compiler warning++-- | Given two irreducible residues /a/ and /b/ of the same type, find+-- an index /m/ such that /a/ + ω[sup /m/]/b/ = 0000. If no such index+-- exists, find an index /m/ such that /a/ + ω[sup /m/]/b/ = 1111.+residue_offset :: Omega Z2 -> Omega Z2 -> Int+residue_offset a b = (residue_shift a - residue_shift b) `mod` 4++-- | Check whether a residue is reducible. A residue /r/ is called /reducible/+-- if it is of the form /r/ = √2 ⋅ /r/', i.e., /r/ ∈ {0000, 0101, 1010, 1111}.+reducible :: Omega Z2 -> Bool+reducible (Omega a b c d) = (a==c) && (b==d)++-- ----------------------------------------------------------------------+-- * Exact synthesis++-- | Perform a single row operation as in Lemma 4, applied to rows /i/+-- and /j/. The entries at rows /i/ and /j/ are /x/ and /y/,+-- respectively, with respective residues /a/ and /b/. A precondition+-- is that /x/ and /y/ are of the same residue type. Returns a list of+-- two-level operations that decreases the denominator exponent.+row_step :: ((Index, Omega Z2, ZOmega), (Index, Omega Z2, ZOmega)) -> [TwoLevel]+row_step ((i,a,x), (j,b,y))+ | reducible a && reducible b = []+ | offs /= 0 = (TL_T offs i j) : row_step ((i,a,x), (j,b',y'))+ | otherwise = (TL_H i j) : row_step ((i,a1,x1), (j,b1,y1))+ where+ offs = residue_offset b a+ y' = omega_power (-offs) y+ b' = residue y'+ (x1,y1) = opH_zomega (x,y)+ (a1,b1) = residue (x1,y1)++-- | Row reduction: Given a unit column vector /v/, generate a+-- sequence of two-level operators that reduces the /i/th standard+-- basis vector /e/[sub /i/] to /v/. Any rows that are already 0 in+-- both vectors are guaranteed not to be touched.+reduce_column :: (Nat n) => Matrix n One (DOmega) -> Index -> [TwoLevel]+reduce_column v i = aux w k where+ vlist = list_of_vector (vector_head (unMatrix v))+ (w, k) = denomexp_decompose vlist+ aux w 0 = m1 ++ m2 where+ j = case findIndices (/= 0) w of+ [j] -> j+ _ -> error "reduce_column: not a unit vector"+ wj = w !! j+ l = case log_omega wj of+ Just l -> l+ Nothing -> error "reduce_column: not a unit vector"+ m1 = if i==j then [] else [TL_X i j]+ m2 = [TL_omega l i]+ aux w k = gates ++ aux w' (k-1) where+ res = residue w+ idx_res = zip3 [0..] res w+ res1010 = [ (i,a,x) | (i,a,x) <- idx_res, residue_type a == RT_1010 ]+ res0001 = [ (i,a,x) | (i,a,x) <- idx_res, residue_type a == RT_0001 ]+ res1010_pairs = case list_pairs res1010 of+ (p, Nothing) -> p+ _ -> error "reduce_column: not a unit vector"+ res0001_pairs = case list_pairs res0001 of+ (p, Nothing) -> p+ _ -> error "reduce_column: not a unit vector"+ m1010 = concat $ map row_step res1010_pairs+ m0001 = concat $ map row_step res0001_pairs+ gates = m1010 ++ m0001+ w' = map (reduce_ZOmega) (apply_twolevels_zomega (invert_twolevels gates) w)++-- | Input an exact /n/×/n/ unitary operator with coefficients in+-- [bold D][ω], and output an equivalent sequence of two-level+-- operators. This is the algorithm from the Giles-Selinger paper. It+-- has superexponential complexity.+-- +-- Note: the list of 'TwoLevel' operators will be returned in+-- right-to-left order, i.e., as in the mathematical notation for+-- matrix multiplication. This is the opposite of the quantum circuit+-- notation.+synthesis_nqubit :: (Nat n) => Matrix n n DOmega -> [TwoLevel]+synthesis_nqubit m = aux (unMatrix m) 0 where+ aux :: (Nat m) => Vector n (Vector m DOmega) -> Index -> [TwoLevel]+ aux Nil i = []+ aux (c `Cons` cs) i = gates ++ aux (unMatrix m') (i+1)+ where+ gates = reduce_column (column_matrix c) i+ gates_matrix = matrix_of_twolevels (invert_twolevels gates)+ m' = gates_matrix .*. (Matrix cs)++-- ----------------------------------------------------------------------+-- * Alternative algorithm+ +-- $ Section 6 of the Giles-Selinger paper mentions an alternate+-- version of the decomposition algorithm. It requires no ancillas,+-- provided that the determinant of the operator permits this.+ +-- | Symbolic representation of one- and two-level operators, with an+-- alternate set of generators.+-- +-- Note: when we use a list of 'TwoLevel' operators to express a+-- sequence of operators, the operators are meant to be applied+-- right-to-left, i.e., as in the mathematical notation for matrix+-- multiplication. This is the opposite of the quantum circuit+-- notation.+data TwoLevelAlt =+ TL_iX Index Index -- ^ /iX/[sub /i/,/j/].+ | TL_TiHT Int Index Index -- ^ (/T/[super −/m/](iH)T[super /m/])[sub /i/,/j/].+ | TL_W Int Index Index -- ^ /W/[super /m/][sub /i/,/j/].+ | TL_omega_alt Int Index -- ^ (ω[sub /i/])[super /m/].+ deriving (Show, Eq)++-- | Convert from the alternate generators to the original generators.+twolevels_of_twolevelalts :: [TwoLevelAlt] -> [TwoLevel]+twolevels_of_twolevelalts [] = []+twolevels_of_twolevelalts (TL_iX j l : t) = + TL_X j l : TL_omega 2 j : TL_omega 2 l : twolevels_of_twolevelalts t+twolevels_of_twolevelalts (TL_TiHT m j l : t) =+ TL_T (-m) j l : TL_H j l : TL_omega 2 j : TL_omega 2 l : TL_T m j l : twolevels_of_twolevelalts t+twolevels_of_twolevelalts (TL_W m j l : t) =+ TL_omega m j : TL_omega (-m) l : twolevels_of_twolevelalts t+twolevels_of_twolevelalts (TL_omega_alt m j : t) =+ TL_omega m j : twolevels_of_twolevelalts t++-- | Invert a list of 'TwoLevelAlt' operators, and convert the output+-- to a list of 'TwoLevel' operators.+invert_twolevels_alt :: [TwoLevelAlt] -> [TwoLevel]+invert_twolevels_alt = invert_twolevels . twolevels_of_twolevelalts++-- | Perform a single row operation as in Lemma 4, applied to rows /i/+-- and /j/, using the generators of Section 6. The entries at rows+-- /i/ and /j/ are /x/ and /y/, respectively, with respective residues+-- /a/ and /b/. A precondition is that /x/ and /y/ are of the same+-- residue type. Returns a list of two-level operations that decreases+-- the denominator exponent.+row_step_alt :: ((Index, Omega Z2, ZOmega), (Index, Omega Z2, ZOmega)) -> [TwoLevelAlt]+row_step_alt ((j,a,x), (l,b,y))+ | reducible a && reducible b = []+ | otherwise = (TL_TiHT m j l) : row_step_alt ((j,a1,x1), (l,b1,y1))+ where+ m = residue_offset a b+ y' = omega_power m y+ (x1,y1') = opH_zomega (-i*x,-i*y')+ y1 = omega_power (-m) y1'+ (a1,b1) = residue (x1,y1)++-- | Row reduction: Given a unit column vector /v/, generate a+-- sequence of two-level operators that reduces the /i/th standard+-- basis vector /e/[sub /i/] to /v/. Any rows that are already 0 in+-- both vectors are guaranteed not to be touched, except possibly row+-- /i/+1 may be multiplied by a scalar.+reduce_column_alt :: (Nat n) => Matrix n One (DOmega) -> Index -> [TwoLevelAlt]+reduce_column_alt v j = aux w k where+ vlist = list_of_vector (vector_head (unMatrix v))+ n = length vlist+ (w, k) = denomexp_decompose vlist+ aux w 0 = m1 ++ m2 where+ l = case findIndices (/= 0) w of+ [l] -> l+ _ -> error "reduce_column: not a unit vector"+ m1 = if j==l then [] else [TL_iX j l]+ wl = if j==l then w !! j else -i*(w !! l)+ m = case log_omega wl of+ Just m -> m+ Nothing -> error "reduce_column: not a unit vector"+ m2 = if j==n-1 then [TL_omega_alt m j] else [TL_W m j (j+1)]+ aux w k = gates ++ aux w' (k-1) where+ res = residue w+ idx_res = zip3 [0..] res w+ res1010 = [ (j,a,x) | (j,a,x) <- idx_res, residue_type a == RT_1010 ]+ res0001 = [ (j,a,x) | (j,a,x) <- idx_res, residue_type a == RT_0001 ]+ res1010_pairs = case list_pairs res1010 of+ (p, Nothing) -> p+ _ -> error "reduce_column: not a unit vector"+ res0001_pairs = case list_pairs res0001 of+ (p, Nothing) -> p+ _ -> error "reduce_column: not a unit vector"+ m1010 = concat $ map row_step_alt res1010_pairs+ m0001 = concat $ map row_step_alt res0001_pairs+ gates = m1010 ++ m0001+ w' = map reduce_ZOmega (apply_twolevels_zomega (invert_twolevels_alt gates) w)++-- | Input an exact /n/×/n/ unitary operator with coefficients in+-- [bold D][ω], and output an equivalent sequence of two-level+-- operators (in the alternative generators, where all but at most one+-- of the generators has determinant 1). This is the algorithm from+-- the Giles-Selinger paper, Section 6. It has superexponential+-- complexity.+-- +-- Note: the list of 'TwoLevelAlt' operators will be returned in+-- right-to-left order, i.e., as in the mathematical notation for+-- matrix multiplication. This is the opposite of the quantum circuit+-- notation.+synthesis_nqubit_alt :: (Nat n) => Matrix n n DOmega -> [TwoLevelAlt]+synthesis_nqubit_alt m = aux (unMatrix m) 0 where+ aux :: (Nat m) => Vector n (Vector m DOmega) -> Index -> [TwoLevelAlt]+ aux Nil i = []+ aux (c `Cons` cs) i = gates ++ aux (unMatrix m') (i+1)+ where+ gates = reduce_column_alt (column_matrix c) i+ gates_matrix = matrix_of_twolevels (invert_twolevels_alt gates)+ m' = gates_matrix .*. (Matrix cs)
+ Quantum/Synthesis/Newsynth.hs view
@@ -0,0 +1,453 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | This module implements an efficient single-qubit Clifford+/T/+-- approximation algorithm. The algorithm is described here:+-- +-- * Peter Selinger. Efficient Clifford+/T/ approximation of+-- single-qubit operators. <http://arxiv.org/abs/1212.6253>.++module Quantum.Synthesis.Newsynth where++import Quantum.Synthesis.Ring+import Quantum.Synthesis.Ring.FixedPrec+import Quantum.Synthesis.Matrix+import Quantum.Synthesis.CliffordT+import Quantum.Synthesis.EuclideanDomain+import Quantum.Synthesis.SymReal++import System.Random+import Data.Number.FixedPrec++-- ----------------------------------------------------------------------+-- * Miscellaneous functions++-- | A useful operation for the 'Maybe' monad, used to ensure that+-- some condition holds (i.e., return 'Nothing' if the condition is+-- false). To be used like this:+-- +-- > do+-- > x <- something+-- > y <- something_else+-- > ensure (x > y)+-- > ...+ensure :: Bool -> Maybe ()+ensure True = Just ()+ensure False = Nothing++-- | Return the head of a list, if non-empty, or else 'Nothing'.+maybe_head :: [a] -> Maybe a+maybe_head [] = Nothing+maybe_head (h:t) = Just h++-- | Exponentiation via repeated squaring, parameterized by a+-- multiplication function and a unit. Given an associative+-- multiplication function @*@ with unit @e@, the function 'power'+-- @(*)@ /e/ /a/ /n/ efficiently computes /a/[sup /n/] = /a/ @*@ (/a/+-- @*@ (… @*@ (/a/ @*@ /e/)…)).+power :: (a -> a -> a) -> a -> a -> Integer -> a+power mul unit = aux where+ aux x n+ | n <= 0 = unit+ | n == 1 = x+ | odd n = x `mul` (x `aux` (n-1))+ | otherwise = y `mul` y where y = x `aux` (n `div` 2)+ +-- | Given positive numbers /b/ and /x/, return (/n/, /r/) such that+-- +-- * /x/ = /r/ /b/[sup /n/] and +-- +-- * 1 ≤ /r/ < /b/. +-- +-- In other words, let /n/ = ⌊log[sub /b/] /x/⌋ and +-- /r/ = /x/ /b/[sup −/n/]. This can be more efficient than 'floor'+-- ('logBase' /b/ /x/) depending on the type; moreover, it also works+-- for exact types such as 'Rational' and 'QRootTwo'.+floorlog :: (Fractional b, Ord b) => b -> b -> (Integer, b)+floorlog b x + | x <= 0 = error "floorlog: argument not positive" + | 1 <= x && x < b = (0, x)+ | 1 <= x*b && x < 1 = (-1, b*x)+ | r < b = (2*n, r)+ | otherwise = (2*n+1, r/b)+ where+ (n, r) = floorlog (b^2) x++-- ----------------------------------------------------------------------+-- * Randomized algorithms++-- | A combinator for turning a probabilistic function that succeeds+-- with some small probability into a probabilistic function that+-- always succeeds, by trying again and again.+keeptrying :: (RandomGen g) => (g -> Maybe a) -> (g -> a)+keeptrying f g = case f g1 of+ Just res -> res+ Nothing -> keeptrying f g2+ where+ (g1, g2) = split g++-- | Like 'keeptrying', but also returns a count of the number of attempts.+keeptrying_count :: (RandomGen g) => (g -> Maybe a) -> (g -> (a, Integer))+keeptrying_count f g = aux g 1 where+ aux g n = case f g1 of+ Just res -> (res, n)+ Nothing -> aux g2 n1+ where+ (g1, g2) = split g+ !n1 = n + 1++-- | A combinator for turning a probabilistic function that succeeds+-- with some small probability into a probabilistic function that+-- succeeds with a higher probability, by repeating it /n/ times. +try_for :: (RandomGen g) => Integer -> (g -> Maybe a) -> (g -> Maybe a)+try_for n f g+ | n <= 0 = Nothing+ | otherwise = case f g1 of+ Just res -> Just res+ Nothing -> try_for (n-1) f g2+ where+ (g1, g2) = split g ++-- ----------------------------------------------------------------------+-- * Square roots in ℤ[√2]++-- | Return a square root of an element of ℤ[√2], if such a square+-- root exists, or else 'Nothing'.+zroottwo_root :: ZRootTwo -> Maybe ZRootTwo+zroottwo_root z@(RootTwo a b) = res where+ d = a^2 - 2*b^2+ r = intsqrt d+ x1 = intsqrt ((a + r) `div` 2)+ x2 = intsqrt ((a - r) `div` 2)+ y1 = intsqrt ((a - r) `div` 4)+ y2 = intsqrt ((a + r) `div` 4)+ w1 = RootTwo x1 y1+ w2 = RootTwo x2 y2+ w3 = RootTwo x1 (-y1)+ w4 = RootTwo x2 (-y2)+ res + | w1*w1 == z = Just w1+ | w2*w2 == z = Just w2+ | w3*w3 == z = Just w3+ | w4*w4 == z = Just w4+ | otherwise = Nothing+ +-- ---------------------------------------------------------------------- +-- * Roots of −1 in ℤ[sub /p/]+ +-- | Input an integer /p/, and maybe output a root of −1 modulo /p/.+-- This succeeds with probability at least 1\/2 if /p/ is a positive+-- prime ≡ 1 (mod 4); otherwise, the success probability is+-- unspecified (and may be 0).+root_minus_one_step :: (RandomGen g) => Integer -> g -> Maybe Integer+root_minus_one_step p g = do+ let (b, _) = randomR (1, p-1) g+ let h = power mul 1 b ((p-1) `div` 4)+ ensure $ h `mul` h == p-1 -- succeeds with probability 1/2+ return h+ where+ mul :: Integer -> Integer -> Integer+ mul a b = (a*b) `mod` p+ +-- | Input a positive prime /p/ ≡ 1 (mod 4), and output a root of −1.+root_minus_one :: (RandomGen g) => Integer -> g -> Integer+root_minus_one p = keeptrying (root_minus_one_step p)++-- ----------------------------------------------------------------------+-- * Solving a Diophantine equation++-- | Input ξ ∈ ℤ[√2], and maybe output some /t/ ∈ ℤ[ω] such that +-- /t/[sup †]/t/ = ξ. If ξ ≥ 0, ξ[sup •] ≥ 0 and /p/ = ξ[sup •]ξ is a+-- prime ≡ 1 (mod 4) in ℤ, then this succeeds with probability at least+-- 1\/2. Otherwise, the success probability is unspecified and may be+-- 0.+dioph_step :: (RandomGen g) => ZRootTwo -> g -> Maybe ZOmega+dioph_step xi g = do+ h <- root_minus_one_step (norm xi) g+ let s = euclid_gcd (fromInteger h+i) (fromZRootTwo xi) :: ZOmega+ ss = zroottwo_of_zomega (adj s * s)+ u = euclid_div xi ss+ v <- zroottwo_root u+ let t = fromZRootTwo v * s+ ensure $ adj t * t == fromZRootTwo xi -- check the answer, just in case+ return t++-- | Input ξ ∈ ℤ[√2] such that ξ ≥ 0, ξ[sup •] ≥ 0, and /p/ = +-- ξ[sup •]ξ is a prime ≡ 1 (mod 4) in ℤ. Output /t/ ∈ ℤ[ω] such that+-- /t/[sup †]/t/ = ξ. If the hypotheses are not satisfied, this will+-- likely loop forever.+dioph :: (RandomGen g) => ZRootTwo -> g -> ZOmega+dioph xi = keeptrying (dioph_step xi)++-- ----------------------------------------------------------------------+-- * Approximations in ℤ[√2]++-- | Input two intervals [/x/₀, /x/₁] ⊆ ℝ and [/y/₀, /y/₁] ⊆ ℝ. Output+-- a list of all points /z/ = /a/ + √2/b/ ∈ ℤ[√2] such that /z/ ∈+-- [/x/₀, /x/₁] and /z/[sup •] ∈ [/y/₀, /y/₁]. The list will be+-- produced lazily, and will be sorted in order of increasing /z/.+-- +-- It is a theorem that there will be at least one solution if ΔxΔy ≥ (1+-- + √2)², and at most one solution if ΔxΔy < 1, where Δx = /x/₁ − /x/₀ ≥ 0+-- and Δy = /y/₁ − /y/₀ ≥ 0. Asymptotically, the expected number of+-- solutions is ΔxΔy/\√8.+-- +-- This function is formulated so that the intervals can be specified+-- exactly (using a type such as 'QRootTwo'), or approximately (using a+-- type such as 'Double' or 'FixedPrec' /e/).+gridpoints :: (RootTwoRing r, Fractional r, Floor r, Ord r) => (r, r) -> (r, r) -> [ZRootTwo]+gridpoints (x0, x1) (y0, y1)+ | dy <= 0 && dx > 0 = + map adj2 $ gridpoints (y0, y1) (x0, x1)+ | dy >= lambda && even n =+ map (lambdainv^n *) $ gridpoints (lambda^n*x0, lambda^n*x1) (lambda'^n*y0, lambda'^n*y1)+ | dy >= lambda && odd n =+ map (lambdainv^n *) $ gridpoints (lambda^n*x0, lambda^n*x1) (lambda'^n*y1, lambda'^n*y0)+ | dy > 0 && dy < 1 && even n = + map (lambda^m *) $ gridpoints (lambdainv^m*x0, lambdainv^m*x1) (lambdainv'^m*y0, lambdainv'^m*y1)+ | dy > 0 && dy < 1 && odd n = + map (lambda^m *) $ gridpoints (lambdainv^m*x0, lambdainv^m*x1) (lambdainv'^m*y1, lambdainv'^m*y0)+ | otherwise =+ [ RootTwo a b | a <- [amin..amax], b <- [bmin a..bmax a], test a b ] + where+ dx = x1 - x0+ dy = y1 - y0+ (n, _) = floorlog lambda dy+ m = -n+ + lambda :: (RootTwoRing r) => r+ lambda = 1 + roottwo+ lambda' :: (RootTwoRing r) => r+ lambda' = 1 - roottwo+ lambdainv :: (RootTwoRing r) => r+ lambdainv = -1 + roottwo+ lambdainv' :: (RootTwoRing r) => r+ lambdainv' = -1 - roottwo++ within x (x0, x1) = x0 <= x && x <= x1+ amin = ceiling_of ((x0 + y0) / 2)+ amax = floor_of ((x1 + y1) / 2)+ bmin a = ceiling_of ((fromInteger a - y1) / roottwo)+ bmax a = floor_of ((fromInteger a - y0) / roottwo)+ test a b = fromZRootTwo x `within` (x0, x1) && fromZRootTwo (adj2 x) `within` (y0, y1)+ where x = RootTwo a b++-- | Input two intervals [/x/₀, /x/₁] ⊆ ℝ and [/y/₀, /y/₁] ⊆ ℝ and a+-- source of randomness. Output a random element /z/ = /a/ + √2/b/+-- ∈ ℤ[√2] such that /z/ ∈ [/x/₀, /x/₁] and /z/[sup •] ∈ [/y/₀,+-- /y/₁]. +-- +-- Note: the randomness will not be uniform. To ensure that the set of+-- solutions is non-empty, we must have ΔxΔy ≥ (1 + √2)², where Δx =+-- /x/₁ − /x/₀ ≥ 0 and Δy = /y/₁ − /y/₀ ≥ 0. If there are no solutions+-- at all, the function will return 'Nothing'.+-- +-- This function is formulated so that the intervals can be specified+-- exactly (using a type such as 'QRootTwo'), or approximately (using a+-- type such as 'Double' or 'FixedPrec' /e/).+gridpoint_random :: (RootTwoRing r, Fractional r, Floor r, Ord r, RandomGen g) => (r, r) -> (r, r) -> g -> Maybe ZRootTwo+gridpoint_random (x0, x1) (y0, y1) g = z+ where+ dx = max 0 (x1 - x0)+ dy = max 0 (y1 - y0)+ area = dx * dy+ n = floor_of (area + 1)+ (i,_) = randomR (0, n-1) g+ r = fromInteger i / fromInteger n+ pts = gridpoints (x0 + r * dx, x1) (y0, y1) ++ gridpoints (x0, x1) (y0, y1)+ z = maybe_head pts++-- | Input an integer /e/, two intervals [/x/₀, /x/₁] ⊆ ℝ and [/y/₀,+-- /y/₁] ⊆ ℝ, and a source of randomness. Output random /z/ = /a/ ++-- √2/b/ ∈ ℤ[√2] such that /a/ + √2/b/ ∈ [/x/₀, /x/₁], /a/ - √2/b/ ∈+-- [/y/₀, /y/₁], and /a/-/e/ is even.+-- +-- Note: the randomness will not be uniform. To ensure that the set of+-- solutions is non-empty, we must have ΔxΔy ≥ 2(√2 + 1)², where Δx =+-- /x/₁ − /x/₀ ≥ 0 and Δy = /y/₁ − /y/₀ ≥ 0. If there are no solutions+-- at all, the function will return 'Nothing'.+-- +-- This function is formulated so that the intervals can be specified+-- exactly (using a type such as 'QRootTwo'), or approximately (using a+-- type such as 'Double' or 'FixedPrec' /e/).+gridpoint_random_parity :: (RootTwoRing r, Fractional r, Floor r, Ord r, RandomGen g) => Integer -> (r, r) -> (r, r) -> g -> Maybe ZRootTwo+gridpoint_random_parity e (x0,x1) (y0,y1) g = do+ z' <- gridpoint_random (x0', x1') (-y1', -y0') g+ return (roottwo * z' + fromInteger e2)+ where + x0' = (x0 - e') / roottwo+ x1' = (x1 - e') / roottwo+ y0' = (y0 - e') / roottwo+ y1' = (y1 - e') / roottwo+ e' = fromInteger e2+ e2 = e `mod` 2++-- ----------------------------------------------------------------------+-- * Approximate synthesis+ +-- ----------------------------------------------------------------------+-- ** The main algorithm++-- | The internal implementation of the approximate synthesis+-- algorithm. The parameters are:+-- +-- * an angle θ, to implement a /R/[sub /z/](θ) = [exp −/i/θ/Z/\/2]+-- gate;+-- +-- * a precision /p/ ≥ 0 in bits, such that ε = 2[sup -/p/];+-- +-- * a source of randomness /g/.+-- +-- With some probability, output a unitary operator in the+-- Clifford+/T/ group that approximates /R/[sub /z/](θ) to within ε in+-- the operator norm. This operator can then be converted to a list of+-- gates with 'to_gates'. Also output log[sub 0.1] of the actual+-- error, or 'Nothing' if the error is 0.+-- +-- This implementation does not use seeding.+-- +-- As a special case, if the /R/[sub /z/](θ) is a Clifford operator+-- (to within the given ε), always return this operator directly.+-- +-- Note: the parameter θ must be of a real number type that has enough+-- precision to perform intermediate calculations; this typically+-- requires precision O(ε[sup 2]). A more user-friendly function that+-- does this automatically is 'newsynth'.+newsynth_step :: forall r g.(RealFrac r, Floating r, RootHalfRing r, Floor r, Adjoint r, RandomGen g) => r -> r -> g -> Maybe (U2 DOmega, Maybe Double)+newsynth_step prec theta = payload where+ -- We are careful to do all computations that depend only on epsilon+ -- and theta (but not g) outside of aux, to avoid re-computing them+ -- with each attempt.+ + -- Calculate ε.+ epsilon = 2 ** (-prec)+ + -- Convert prec to a Double+ dprec = fromRational (toRational prec)+ + -- Determine k.+ const = 3 + 2 * logBase 2 (1 + sqrt 2) :: Double+ k = ceiling (const + 2 * dprec)+ scale = roottwo^k+ + -- Normalize θ to be in [-π/4, π/4].+ n = round(theta / (pi/2))+ theta1 = theta - fromInteger n * pi/2+ + -- Describe the ε-region.+ z @ (x,y) = (cos (theta1 / 2), -sin (theta1 / 2))+ e2 = 1 - epsilon^2/2+ e4 = 1 - epsilon^2/4+ z1 @ (x1,y1) = (e4 * x, e4 * y)+ e' = epsilon / roottwo+ f = e' * sqrt((1+e'/2)*(1-e'/2)) -- == sqrt(1-e4^2)+ w @ (wx,wy) = (-f * y, f * x)+ y_min = y1 - wy+ y_max = y1 + wy+ y'_min = y_min * scale+ y'_max = y_max * scale+ dx = (e4 - e2) * x+ + find_uU_step = + -- As a special case, if (1,0) is in the ε-region, return the+ -- identity operator.+ if x >= e2 then \g -> Just 1 else aux++ -- The rest of the computation depends on the random seed g.+ payload g = do+ uU1 <- find_uU_step g + let uU = correct uU1 n+ let err = calc_error uU theta+ return (uU, err)+ + aux g = do+ -- Find a random grid point in the ε-region.+ let (g0,g1) = split g+ beta <- gridpoint_random (y'_min, y'_max) (-roothalf * scale, roothalf * scale) g0+ let + beta' = fromZRootTwo beta / scale+ tmp = (beta' - e2 * y) / wy+ x0 = e2 * x + tmp * wx+ x1 = x0 + dx+ x0' = x0 * scale+ x1' = x1 * scale+ (g2,g3) = split g1+ RootTwo c _ = beta+ alpha <- gridpoint_random_parity (c+1) (x0', x1') (-roothalf * scale, roothalf * scale) g2+ + -- Calculate u, ξ, and solve Diophantine equation to calculate t.+ let + u = (fromZRootTwo alpha) + i * (fromZRootTwo beta) :: ZOmega+ xi = zroottwo_of_zomega (2^k - u * adj u)+ t <- dioph_step xi g3+ + -- If Diophantine equation solved successfully, calculate matrix U.+ let+ u' = fromZOmega u * roothalf^k :: DOmega+ t' = fromZOmega t * roothalf^k :: DOmega+ uU1 = matrix2x2 (u', -(adj t'))+ (t', (adj u'))+ + return uU1+ + -- Correct for when θ wasn't in [-π/4, π/4].+ correct uU1 n = uU1 * rR^(n `mod` 8) where+ rR = matrix2x2 (omega^7, 0)+ (0, omega)+ + -- Calculate the actual error. Since this is done lazily, this+ -- incurs no overhead in case the error is not actually used.+ calc_error uU theta = log_err where+ uU_fixed = matrix_map fromDOmega uU :: U2 (Cplx r)+ zrot_fixed = zrot theta :: U2 (Cplx r)+ err = sqrt (real (hs_sqnorm (uU_fixed - zrot_fixed)) / 2)+ log_err + | err <= 0 = Nothing+ | otherwise = Just (log_double err / log 0.1)++-- ----------------------------------------------------------------------+-- ** User-friendly functions++-- | A user-friendly interface to the approximate synthesis+-- algorithm. The parameters are:+-- +-- * an angle θ, to implement a /R/[sub /z/](θ) = [exp −/i/θ/Z/\/2]+-- gate;+-- +-- * a precision /b/ ≥ 0 in bits, such that ε = 2[sup -/b/];+-- +-- * a source of randomness /g/.+-- +-- Output a unitary operator in the Clifford+/T/ group that+-- approximates /R/[sub /z/](θ) to within ε in the operator norm. This+-- operator can then be converted to a list of gates with+-- 'to_gates'.+-- +-- This implementation does not use seeding.+-- +-- Note: the argument /theta/ is given as a symbolic real number. It+-- will automatically be expanded to as many digits as are necessary+-- for the internal calculation. In this way, the caller can specify,+-- e.g., an angle of 'pi'\/128 @::@ 'SymReal', without having to worry+-- about how many digits of π to specify.+newsynth :: (RandomGen g) => Double -> SymReal -> g -> U2 DOmega+newsynth prec theta g = m where+ (m, _, _) = newsynth_stats prec theta g++-- | A version of 'newsynth' that also returns some statistics:+-- log[sub 0.1] of the actual approximation error (or 'Nothing' if the+-- error is 0), and the number of candidates tried.+newsynth_stats :: (RandomGen g) => Double -> SymReal -> g -> (U2 DOmega, Maybe Double, Integer)+newsynth_stats prec theta g = dynamic_fixedprec2 digits f prec theta where+ digits = ceiling (10 + 2 * prec * logBase 10 2)+ f prec theta = (m, err, ct) where+ ((m, err), ct) = keeptrying_count (newsynth_step prec theta) g++-- | A version of 'newsynth' that returns a list of gates instead of a+-- matrix. The inputs are the same as for 'newsynth'.+-- +-- Note: the list of gates will be returned in right-to-left order,+-- i.e., as in the mathematical notation for matrix multiplication.+-- This is the opposite of the quantum circuit notation.+newsynth_gates :: (RandomGen g) => Double -> SymReal -> g -> [Gate]+newsynth_gates prec theta g = synthesis_u2 (newsynth prec theta g)
+ Quantum/Synthesis/Ring.hs view
@@ -0,0 +1,1046 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE BangPatterns #-}++-- | This module provides type classes for rings. It also provides+-- several specific instances of rings, such as the ring ℤ₂ of+-- integers modulo 2, the ring ℚ of rational numbers, the ring ℤ[½] of+-- dyadic fractions, the ring ℤ[/i/] of Gaussian integers, the ring+-- ℤ[√2] of quadratic integers with radix 2, and the ring ℤ[ω] of+-- cyclotomic integers of degree 8.++module Quantum.Synthesis.Ring where++import Data.Bits+import Data.Complex+import Data.Ratio++-- ----------------------------------------------------------------------+-- * Rings++-- | A type class to denote rings. We make 'Ring' a synonym of+-- Haskell's 'Num' type class, so that we can use the usual notation+-- '+', '-', '*' for the ring operations. This is not a perfect fit,+-- because Haskell's 'Num' class also contains two non-ring operations+-- 'abs' and 'signum'. By convention, for rings where these notions+-- don't make sense (or are inconvenient to define), we set 'abs' /x/+-- = /x/ and 'signum' /x/ = 1.++class (Num a) => Ring a+instance (Num a) => Ring a++-- ----------------------------------------------------------------------+-- * Rings with particular elements++-- $ We define several classes of rings with special elements.++-- ----------------------------------------------------------------------+-- ** Rings with ½++-- | A type class for rings that contain ½.+class (Ring a) => HalfRing a where+ -- | The value ½.+ half :: a++instance HalfRing Double where+ half = 0.5++instance HalfRing Float where+ half = 0.5++instance HalfRing Rational where+ half = 1%2++instance (HalfRing a, RealFloat a) => HalfRing (Complex a) where+ half = half :+ 0++-- ----------------------------------------------------------------------+-- ** Rings with √2++-- | A type class for rings that contain √2.+class (Ring a) => RootTwoRing a where+ -- | The square root of 2.+ roottwo :: a+ +instance RootTwoRing Double where+ roottwo = sqrt 2++instance RootTwoRing Float where+ roottwo = sqrt 2++instance (RootTwoRing a, RealFloat a) => RootTwoRing (Complex a) where+ roottwo = roottwo :+ 0++-- ----------------------------------------------------------------------+-- ** Rings with 1\/√2++-- | A type class for rings that contain 1\/√2.+class (HalfRing a, RootTwoRing a) => RootHalfRing a where+ -- | The square root of ½.+ roothalf :: a+ +instance RootHalfRing Double where+ roothalf = sqrt 0.5++instance RootHalfRing Float where+ roothalf = sqrt 0.5++instance (RootHalfRing a, RealFloat a) => RootHalfRing (Complex a) where+ roothalf = roothalf :+ 0+++-- ----------------------------------------------------------------------+-- ** Rings with /i/++-- | A type class for rings that contain a square root of -1.+class (Ring a) => ComplexRing a where+ -- | The complex unit.+ i :: a+ +instance (Ring a, RealFloat a) => ComplexRing (Complex a) where+ i = 0 :+ 1++-- ----------------------------------------------------------------------+-- ** Rings with ω++-- | A type class for rings that contain a square root of /i/, or+-- equivalently, a fourth root of −1.+class (Ring a) => OmegaRing a where+ -- | The square root of /i/.+ omega :: a+ +instance (ComplexRing a, RootHalfRing a) => OmegaRing a where+ omega = roothalf * (1 + i)++-- ----------------------------------------------------------------------+-- * Rings with particular automorphisms++-- ----------------------------------------------------------------------+-- ** Rings with complex conjugation++-- | A type class for rings with complex conjugation, i.e., an+-- automorphism mapping /i/ to −/i/. +-- +-- When instances of this type class are vectors or matrices, the+-- conjugation also exchanges the roles of rows and columns (in other+-- words, it is the adjoint).+-- +-- For rings that are not complex, the conjugation can be defined to+-- be the identity function.+class Adjoint a where+ -- | Compute the adjoint (complex conjugate transpose).+ adj :: a -> a++instance Adjoint Integer where+ adj x = x+ +instance Adjoint Int where+ adj x = x+ +instance Adjoint Double where+ adj x = x+ +instance Adjoint Float where+ adj x = x+ +instance Adjoint Rational where + adj x = x+ +instance (Adjoint a, Ring a) => Adjoint (Complex a) where+ adj (a :+ b) = adj a :+ (-adj b)++-- ----------------------------------------------------------------------+-- ** Rings with √2-conjugation++-- | A type class for rings with a √2-conjugation, i.e., an+-- automorphism mapping √2 to −√2. +-- +-- When instances of this type class are vectors or matrices, the+-- √2-conjugation does /not/ exchange the roles of rows and columns.+-- +-- For rings that have no √2, the conjugation can be defined to be the+-- identity function.+class Adjoint2 a where+ -- | Compute the adjoint, mapping /a/ + /b/√2 to /a/ −/b/√2.+ adj2 :: a -> a++instance Adjoint2 Integer where+ adj2 x = x++instance Adjoint2 Int where+ adj2 x = x+ +instance Adjoint2 Rational where + adj2 x = x+ +-- ----------------------------------------------------------------------+-- * Normed rings++-- | A (number-theoretic) /norm/ on a ring /R/ is a function /N/ : /R/+-- → ℤ such that /N/(/rs/) = /N/(/r/)/N/(/s/), for all /r/, /s/ ∈ /R/.+-- The norm also satisfies /N/(/r/) = 0 iff /r/ = 0, and /N/(/r/) = ±1+-- iff /r/ is a unit of the ring.+class (Ring r) => NormedRing r where+ norm :: r -> Integer+ +instance NormedRing Integer where+ norm x = x+ +-- ----------------------------------------------------------------------+-- * Floor and ceiling+ +-- | The 'floor' and 'ceiling' functions provided by the standard+-- Haskell libraries are predicated on many unnecessary assumptions.+-- This type class provides an alternative.+-- +-- Minimal complete definition: 'floor_of' or 'ceiling_of'.+class (Ring r) => Floor r where+ -- | Compute the floor of /x/, i.e., the greatest integer /n/ such+ -- that /n/ ≤ /x/.+ floor_of :: r -> Integer+ floor_of x = -(ceiling_of (-x))+ -- | Compute the ceiling of /x/, i.e., the least integer /n/ such+ -- that /x/ ≤ /n/.+ ceiling_of :: r -> Integer+ ceiling_of x = -(floor_of (-x))++instance Floor Integer where+ floor_of = id+ ceiling_of = id++instance Floor Rational where+ floor_of = floor+ ceiling_of = ceiling++instance Floor Float where+ floor_of = floor+ ceiling_of = ceiling++instance Floor Double where+ floor_of = floor+ ceiling_of = ceiling++-- ----------------------------------------------------------------------+-- * Particular rings++-- ----------------------------------------------------------------------+-- ** The ring ℤ₂ of integers modulo 2++-- | The ring ℤ₂ of integers modulo 2. +data Z2 = Even | Odd+ deriving (Eq)+ +instance Show Z2 where+ show Even = "0"+ show Odd = "1"++instance Num Z2 where+ Even + x = x+ x + Even = x+ Odd + Odd = Even+ Even * x = Even+ x * Even = Even+ Odd * Odd = Odd+ negate x = x+ fromInteger n = if even n then Even else Odd+ abs x = x+ signum x = 1++instance Adjoint Z2 where+ adj x = x++instance Adjoint2 Z2 where+ adj2 x = x++-- ----------------------------------------------------------------------+-- ** The ring [bold D] of dyadic fractions++-- | A dyadic fraction is a rational number whose denominator is a+-- power of 2. We denote the dyadic fractions by [bold D] = ℤ[½].+-- +-- We internally represent a dyadic fraction /a/\/2[sup /n/] as a pair+-- (/a/,/n/). Note that this representation is not unique. When it is+-- necessary to choose a canonical representative, we choose the least+-- possible /n/≥0.+data Dyadic = Dyadic !Integer !Integer++-- | Given a dyadic fraction /r/, return (/a/,/n/) such that /r/ =+-- /a/\/2[sup /n/], where /n/≥0 is chosen as small as possible.+decompose_dyadic :: Dyadic -> (Integer, Integer)+decompose_dyadic (Dyadic a n) + | a == 0 = (0, 0)+ | n >= k = (b, n-k)+ | otherwise = (shiftL b (fromInteger (k-n)), 0)+ where+ (b,k) = lobit a++-- | Given a dyadic fraction /r/ and an integer /k/≥0, such that /a/ =+-- /r/2[sup /k/] is an integer, return /a/. If /a/ is not an integer,+-- the behavior is undefined.+integer_of_dyadic :: Dyadic -> Integer -> Integer+integer_of_dyadic (Dyadic a n) k =+ shift a (fromInteger (k-n))++instance Real Dyadic where+ toRational (Dyadic a n) + | n >= 0 = a % 2^n+ | otherwise = a * 2^(-n) % 1++instance Show Dyadic where+ showsPrec d a = showsPrec_rational d (toRational a)++instance Eq Dyadic where+ Dyadic a n == Dyadic b m = a * 2^(k-n) == b * 2^(k-m) where+ k = max n m++instance Ord Dyadic where+ compare (Dyadic a n) (Dyadic b m) = compare (a * 2^(k-n)) (b * 2^(k-m)) where+ k = max n m++instance Num Dyadic where+ Dyadic a n + Dyadic b m + | n < m = Dyadic c m+ | otherwise = Dyadic d n+ where+ c = shiftL a (fromInteger (m-n)) + b+ d = a + shiftL b (fromInteger (n-m))+ Dyadic a n * Dyadic b m = Dyadic (a*b) (n+m)+ negate (Dyadic a n) = Dyadic (-a) n+ abs x = if x >= 0 then x else -x+ signum x = case compare 0 x of { LT -> 1; EQ -> 0; GT -> -1 }+ fromInteger n = Dyadic n 0++instance HalfRing Dyadic where+ half = Dyadic 1 1++instance Adjoint Dyadic where+ adj x = x++instance Adjoint2 Dyadic where+ adj2 x = x++-- | The unique ring homomorphism from ℤ[½] to any ring containing+-- ½. This exists because ℤ[½] is the free such ring.++-- Implementation note: we can't just use fromInteger a * half^n,+-- because this can give potentially bad round-off errors in case+-- half^n underflows in the target type. Moreover, this does not work+-- if n is negative.+fromDyadic :: (HalfRing a) => Dyadic -> a+fromDyadic x = aux (fromInteger a) n where+ (a,n) = decompose_dyadic x+ aux !a !n+ | n>0 = aux (half*a) (n-1)+ | n==0 = a+ | otherwise = aux (2*a) (n+1)++-- ----------------------------------------------------------------------+-- ** The ring ℚ of rational numbers++-- | We define our own variant of the rational numbers, which is an+-- identical copy of the type 'Rational' from the standard Haskell+-- library, except that it has a more sensible 'Show' instance.+newtype Rationals = ToRationals { unRationals :: Rational }+ deriving (Num, Eq, Ord, Fractional, Real, RealFrac, HalfRing, Adjoint, Adjoint2, ToQOmega, Floor)++-- | An auxiliary function for printing rational numbers, using+-- correct precedences, and omitting denominators of 1.+showsPrec_rational :: (Show a, Integral a) => Int -> Ratio a -> ShowS+showsPrec_rational d a+ | denom == 1 = showsPrec d numer+ | numer < 0 = showParen (d >= 7) $ showString "-" . showsPrec_rational 7 (-a)+ | otherwise = showParen (d >= 8) $+ showsPrec 7 numer . showString "/" . showsPrec 8 denom+ where+ numer = numerator a+ denom = denominator a++instance Show Rationals where+ showsPrec d (ToRationals a) = showsPrec_rational d a++-- | Conversion from 'Rationals' to any 'Fractional' type.+fromRationals :: (Fractional a) => Rationals -> a+fromRationals = fromRational . unRationals++-- ----------------------------------------------------------------------+-- ** The ring /R/[√2]+ +-- | The ring /R/[√2], where /R/ is any ring. The value 'RootTwo' /a/+-- /b/ represents /a/ + /b/ √2.+data RootTwo a = RootTwo !a !a+ deriving (Eq)++instance (Eq a, Num a) => Num (RootTwo a) where+ RootTwo a b + RootTwo a' b' = RootTwo a'' b'' where+ a'' = a + a'+ b'' = b + b'+ RootTwo a b * RootTwo a' b' = RootTwo a'' b'' where+ a'' = a * a' + 2 * b * b'+ b'' = a * b' + a' * b+ negate (RootTwo a b) = RootTwo a' b' where+ a' = -a+ b' = -b+ fromInteger n = RootTwo n' 0 where+ n' = fromInteger n+ abs f = f * signum f+ signum f@(RootTwo a b)+ | sa == 0 && sb == 0 = 0+ | sa /= -1 && sb /= -1 = 1+ | sa /= 1 && sb /= 1 = -1+ | sa /= -1 && sb /= 1 && signum (a*a - 2*b*b) /= -1 = 1+ | sa /= 1 && sb /= -1 && signum (a*a - 2*b*b) /= 1 = 1+ | otherwise = -1+ where+ sa = signum a+ sb = signum b++instance (Eq a, Ring a) => Ord (RootTwo a) where+ x <= y = signum (y-x) /= (-1)+ +instance (Show a, Eq a, Ring a) => Show (RootTwo a) where+ showsPrec d (RootTwo a 0) = showsPrec d a+ showsPrec d (RootTwo 0 1) = showString "roottwo"+ showsPrec d (RootTwo 0 (-1)) = showParen (d >= 7) $ showString "-roottwo"+ showsPrec d (RootTwo 0 b) = showParen (d >= 8) $ + showsPrec 7 b . showString "*roottwo"+ showsPrec d (RootTwo a b) | signum b == 1 = showParen (d >= 7) $+ showsPrec 6 a . showString " + " . showsPrec 6 (RootTwo 0 b)+ showsPrec d (RootTwo a b) | otherwise = showParen (d >= 7) $+ showsPrec 6 a . showString " - " . showsPrec 7 (RootTwo 0 (-b))++instance (Eq a, Fractional a) => Fractional (RootTwo a) where+ recip (RootTwo a b) = RootTwo (a/k) (-b/k) where+ k = a^2 - 2*b^2+ fromRational r = RootTwo (fromRational r) 0++instance (Eq a, Ring a) => RootTwoRing (RootTwo a) where+ roottwo = RootTwo 0 1++instance (Eq a, HalfRing a) => HalfRing (RootTwo a) where+ half = RootTwo half 0+ +instance (Eq a, HalfRing a) => RootHalfRing (RootTwo a) where+ roothalf = RootTwo 0 half+ +instance (Eq a, ComplexRing a) => ComplexRing (RootTwo a) where+ i = RootTwo i 0++instance (Adjoint a) => Adjoint (RootTwo a) where + adj (RootTwo a b) = RootTwo (adj a) (adj b)++instance (Adjoint2 a, Num a) => Adjoint2 (RootTwo a) where + adj2 (RootTwo a b) = RootTwo (adj2 a) (-adj2 b)++instance (Eq a, NormedRing a) => NormedRing (RootTwo a) where+ norm (RootTwo a b) = (norm a)^2 - 2 * (norm b)^2++-- ----------------------------------------------------------------------+-- ** The ring ℤ[√2]++-- | The ring ℤ[√2].+type ZRootTwo = RootTwo Integer++-- | The unique ring homomorphism from ℤ[√2] to any ring containing+-- √2. This exists because ℤ[√2] is the free such ring.+fromZRootTwo :: (RootTwoRing a) => ZRootTwo -> a+fromZRootTwo (RootTwo x y) = fromInteger x + roottwo * fromInteger y++-- ----------------------------------------------------------------------+-- ** The ring [bold D][√2]++-- | The ring [bold D][√2] = ℤ[1\/√2]. +type DRootTwo = RootTwo Dyadic++-- | The unique ring homomorphism from [bold D][√2] to any ring containing+-- 1\/√2. This exists because [bold D][√2] = ℤ[1\/√2] is the free such ring.+fromDRootTwo :: (RootHalfRing a) => DRootTwo -> a+fromDRootTwo (RootTwo x y) = fromDyadic x + roottwo * fromDyadic y++-- ----------------------------------------------------------------------+-- ** The field ℚ[√2]++-- | The field ℚ[√2].+type QRootTwo = RootTwo Rationals++instance Floor QRootTwo where+ floor_of x@(RootTwo a b)+ | r'+1 <= x = r+1+ | r' <= x = r+ | otherwise = r-1 + where + a' = floor a+ b' = intsqrt (floor (2 * b^2))+ r | b >= 0 = a' + b'+ | otherwise = a' - b'+ r' = fromInteger r++-- | The unique ring homomorphism from ℚ[√2] to any ring containing+-- the rational numbers and √2. This exists because ℚ[√2] is the free+-- such ring.+fromQRootTwo :: (RootTwoRing a, Fractional a) => QRootTwo -> a+fromQRootTwo (RootTwo x y) = fromRationals x + roottwo * fromRationals y++-- ----------------------------------------------------------------------+-- ** The ring /R/[/i/]++-- | The ring /R/[/i/], where /R/ is any ring. The reason we do not+-- use the 'Complex' /a/ type from the standard Haskell libraries is+-- that it assumes too much, for example, it assumes /a/ is a member+-- of the 'RealFloat' class. Also, this allows us to define a more+-- sensible 'Show' instance.+data Cplx a = Cplx !a !a+ deriving (Eq)++instance (Eq a, Show a, Num a) => Show (Cplx a) where+ showsPrec d (Cplx a 0) = showsPrec d a+ showsPrec d (Cplx 0 1) = showString "i"+ showsPrec d (Cplx 0 (-1)) = showParen (d >= 7) $ showString "-i"+ showsPrec d (Cplx 0 b) = showParen (d >= 8) $ + showsPrec 7 b . showString "*i"+ showsPrec d (Cplx a b) | signum b == 1 = showParen (d >= 7) $+ showsPrec 6 a . showString " + " . showsPrec 6 (Cplx 0 b)+ showsPrec d (Cplx a b) | otherwise = showParen (d >= 7) $+ showsPrec 6 a . showString " - " . showsPrec 7 (Cplx 0 (-b))++instance (Num a) => Num (Cplx a) where+ Cplx a b + Cplx a' b' = Cplx a'' b'' where+ a'' = a + a'+ b'' = b + b'+ Cplx a b * Cplx a' b' = Cplx a'' b'' where+ a'' = a * a' - b * b'+ b'' = a * b' + a' * b+ negate (Cplx a b) = Cplx a' b' where+ a' = -a+ b' = -b+ fromInteger n = Cplx n' 0 where+ n' = fromInteger n+ abs x = x+ signum x = 1++instance (Fractional a) => Fractional (Cplx a) where+ recip (Cplx a b) = Cplx (a/d) (-b/d) where+ d = a^2 + b^2+ fromRational a = Cplx (fromRational a) 0++instance (Ring a) => ComplexRing (Cplx a) where+ i = Cplx 0 1++instance (HalfRing a) => HalfRing (Cplx a) where+ half = Cplx half 0++instance (RootHalfRing a) => RootHalfRing (Cplx a) where+ roothalf = Cplx roothalf 0++instance (RootTwoRing a) => RootTwoRing (Cplx a) where+ roottwo = Cplx roottwo 0++instance (Adjoint a, Ring a) => Adjoint (Cplx a) where+ adj (Cplx a b) = (Cplx (adj a) (-(adj b)))++instance (Adjoint2 a, Ring a) => Adjoint2 (Cplx a) where+ adj2 (Cplx a b) = (Cplx (adj2 a) (adj2 b))++instance (NormedRing a) => NormedRing (Cplx a) where+ norm (Cplx a b) = (norm a)^2 + (norm b)^2++-- ----------------------------------------------------------------------+-- ** The ring ℤ[/i/] of Gaussian integers++-- | The ring ℤ[/i/] of Gaussian integers.+type ZComplex = Cplx Integer++-- | The unique ring homomorphism from ℤ[/i/] to any ring containing+-- /i/. This exists because ℤ[/i/] is the free such ring.+fromZComplex :: (ComplexRing a) => ZComplex -> a+fromZComplex (Cplx a b) = fromInteger a + i * fromInteger b++-- ----------------------------------------------------------------------+-- ** The ring [bold D][/i/]++-- | The ring [bold D][/i/] = ℤ[½, /i/] of Gaussian dyadic fractions.+type DComplex = Cplx Dyadic++-- | The unique ring homomorphism from [bold D][/i/] to any ring containing+-- ½ and /i/. This exists because [bold D][/i/] is the free such ring.+fromDComplex :: (ComplexRing a, HalfRing a) => DComplex -> a+fromDComplex (Cplx a b) = fromDyadic a + i * fromDyadic b++-- ----------------------------------------------------------------------+-- ** The ring ℚ[/i/] of Gaussian rationals++-- | The ring ℚ[/i/] of Gaussian rationals.+type QComplex = Cplx Rationals++-- | The unique ring homomorphism from ℚ[/i/] to any ring containing+-- the rational numbers and /i/. This exists because ℚ[/i/] is the+-- free such ring.+fromQComplex :: (ComplexRing a, Fractional a) => QComplex -> a+fromQComplex (Cplx a b) = fromRationals a + i * fromRationals b++-- ----------------------------------------------------------------------+-- ** The ring [bold D][√2, /i/]++-- | The ring [bold D][√2, /i/] = ℤ[1\/√2, /i/].+type DRComplex = Cplx DRootTwo++-- | The unique ring homomorphism from [bold D][√2, /i/] to any ring+-- containing 1\/√2 and /i/. This exists because [bold D][√2, /i/] =+-- ℤ[1\/√2, /i/] is the free such ring.+fromDRComplex :: (RootHalfRing a, ComplexRing a) => DRComplex -> a+fromDRComplex (Cplx a b) = fromDRootTwo a + i * fromDRootTwo b++-- ----------------------------------------------------------------------+-- ** The ring ℚ[√2, /i/]++-- | The field ℚ[√2, /i/].+type QRComplex = Cplx QRootTwo++-- | The unique ring homomorphism from ℚ[√2, /i/] to any ring+-- containing the rational numbers, √2, and /i/. This exists because+-- ℚ[√2, /i/] is the free such ring.+fromQRComplex :: (RootTwoRing a, ComplexRing a, Fractional a) => QRComplex -> a+fromQRComplex (Cplx a b) = fromQRootTwo a + i * fromQRootTwo b++-- ----------------------------------------------------------------------+-- ** The ring ℂ of complex numbers++-- $ We provide two versions of the complex numbers using floating+-- point arithmetic.++-- | Double precision complex floating point numbers.+type CDouble = Cplx Double++-- | Single precision complex floating point numbers.+type CFloat = Cplx Float++-- ----------------------------------------------------------------------+-- ** The ring /R/[ω]++-- | The ring /R/[ω], where /R/ is any ring, and ω = [exp iπ/4] is an+-- 8th root of unity. The value 'Omega' /a/ /b/ /c/ /d/ represents+-- /a/ω[sup 3]+/b/ω[sup 2]+/c/ω+/d/.+data Omega a = Omega !a !a !a !a+ deriving (Eq)++-- | An inverse to the embedding /R/ ↦ /R/[ω]: return the \"real+-- rational\" part. +-- In other words, map /a/ω[sup 3]+/b/ω[sup 2]+/c/ω+/d/ to /d/.+omega_real :: Omega a -> a+omega_real (Omega a b c d) = d++instance (Show a, Ring a) => Show (Omega a) where+ showsPrec p (Omega a b c d) = + showParen (p >= 11) $ showString "Omega " . + showsPrec 11 a . showString " " . + showsPrec 11 b . showString " " . + showsPrec 11 c . showString " " . + showsPrec 11 d++instance (Num a) => Num (Omega a) where+ Omega a b c d + Omega a' b' c' d' = Omega a'' b'' c'' d'' where+ a'' = a + a'+ b'' = b + b'+ c'' = c + c'+ d'' = d + d'+ Omega a b c d * Omega a' b' c' d' = Omega a'' b'' c'' d'' where + a'' = a*d' + b*c' + c*b' + d*a'+ b'' = b*d' + c*c' + d*b' - a*a'+ c'' = c*d' + d*c' - a*b' - b*a'+ d'' = d*d' - a*c' - b*b' - c*a'+ negate (Omega a b c d) = Omega (-a) (-b) (-c) (-d) where+ fromInteger n = Omega 0 0 0 n' where+ n' = fromInteger n+ abs x = x+ signum x = 1++instance (Fractional a) => Fractional (Omega a) where+ recip (Omega a b c d) = x1 * x2 * x3 * Omega 0 0 0 (1/denom)+ where+ x1 = Omega (-c) (-b) (-a) d+ x2 = Omega (-a) b (-c) d+ x3 = Omega c (-b) a d+ denom = (a^2+b^2+c^2+d^2)^2-2*(a*b+b*c+c*d-d*a)^2+ fromRational r = fromInteger a / fromInteger b where+ a = numerator r+ b = denominator r++instance (HalfRing a) => HalfRing (Omega a) where+ half = Omega 0 0 0 half++instance (HalfRing a) => RootHalfRing (Omega a) where+ roothalf = Omega (-half) 0 half 0++instance (Ring a) => RootTwoRing (Omega a) where+ roottwo = Omega (-1) 0 1 0++instance (Ring a) => ComplexRing (Omega a) where+ i = Omega 0 1 0 0++instance (Adjoint a, Ring a) => Adjoint (Omega a) where+ adj (Omega a b c d) = Omega (-(adj c)) (-(adj b)) (-(adj a)) (adj d)++instance (Adjoint2 a, Ring a) => Adjoint2 (Omega a) where+ adj2 (Omega a b c d) = Omega (-adj2 a) (adj2 b) (-adj2 c) (adj2 d)++instance (NormedRing a) => NormedRing (Omega a) where+ norm (Omega x y z w) = (a^2+b^2+c^2+d^2)^2-2*(a*b+b*c+c*d-d*a)^2+ where+ a = norm x+ b = norm y+ c = norm z+ d = norm w++-- This is an undecidable instance, but is not overlapping. Note: we+-- do not declare OmegaRing (Omega a), because this usually follows+-- from (ComplexRing a, RootHalfRing a). But there are some+-- exceptions, such as OmegaRing (Omega Z2) and OmegaRing (Omega+-- Integer).+instance OmegaRing (Omega Z2) where+ omega = Omega 0 0 1 0++-- ----------------------------------------------------------------------+-- ** The ring ℤ[ω]++-- | The ring ℤ[ω] of /cyclotomic integers/ of degree 8. Such rings+-- were first studied by Kummer around 1840, and used in his proof of+-- special cases of Fermat's Last Theorem. See also:+-- +-- * <http://fermatslasttheorem.blogspot.com/2006/05/basic-properties-of-cyclotomic.html>+-- +-- * <http://fermatslasttheorem.blogspot.com/2006/02/cyclotomic-integers.html>+-- +-- * Harold M. Edwards, \"Fermat's Last Theorem: A Genetic+-- Introduction to Algebraic Number Theory\".+type ZOmega = Omega Integer++-- | The unique ring homomorphism from ℤ[ω] to any ring containing+-- ω. This exists because ℤ[ω] is the free such ring.+fromZOmega :: (OmegaRing a) => ZOmega -> a+fromZOmega (Omega a b c d) = fromInteger a * omega^3 + fromInteger b * omega^2 + fromInteger c * omega + fromInteger d++-- This is an undecidable instance, but is not overlapping.+instance OmegaRing ZOmega where+ omega = Omega 0 0 1 0++-- | Inverse of the embedding ℤ[√2] → ℤ[ω]. Note that ℤ[√2] = ℤ[ω] ∩+-- ℝ. This function takes an element of ℤ[ω] that is real, and+-- converts it to an element of ℤ[√2]. It throws an error if the input+-- is not real.+zroottwo_of_zomega :: ZOmega -> ZRootTwo+zroottwo_of_zomega (Omega a b c d)+ | a == -c && b == 0 = RootTwo d c+ | otherwise = error "zroottwo_of_zomega: non-real value"+ +-- ----------------------------------------------------------------------+-- ** The ring [bold D][ω]++-- | The ring [bold D][ω]. Here [bold D]=ℤ[½] is the ring of dyadic+-- fractions. In fact, [bold D][ω] is isomorphic to the ring [bold D][√2,+-- i], but they have different 'Show' instances.+type DOmega = Omega Dyadic++-- | The unique ring homomorphism from [bold D][ω] to any ring containing+-- 1\/√2 and /i/. This exists because [bold D][ω] is the free such ring.+fromDOmega :: (RootHalfRing a, ComplexRing a) => DOmega -> a+fromDOmega (Omega a b c d) = fromDyadic a * omega^3 + fromDyadic b * omega^2 + fromDyadic c * omega + fromDyadic d++-- This is an overlapping instance. It is nicer to show an element of+-- D[ω] by pulling out a common denominator exponent. But in cases+-- where the typechecker cannot infer this, then it will just fall+-- back to the more general method.+instance Show DOmega where+ showsPrec = showsPrec_DenomExp+ +-- This is an overlapping instance. See previous comment.+instance Show DRComplex where+ showsPrec = showsPrec_DenomExp++-- ----------------------------------------------------------------------+-- ** The field ℚ[ω]++-- | The field ℚ[ω] of /cyclotomic rationals/ of degree 8.+type QOmega = Omega Rationals++-- | The unique ring homomorphism from ℚ[ω] to any ring containing the+-- rational numbers, √2, and /i/. This exists because ℚ[ω] is the free+-- such ring.+fromQOmega :: (RootHalfRing a, ComplexRing a, Fractional a) => QOmega -> a+fromQOmega (Omega a b c d) = fromRationals a * omega^3 + fromRationals b * omega^2 + fromRationals c * omega + fromRationals d++-- ----------------------------------------------------------------------+-- * Conversion to dyadic++-- | A type class relating \"rational\" types to their dyadic+-- counterparts.+class ToDyadic a b | a -> b where+ -- | Convert a \"rational\" value to a \"dyadic\" value, if the+ -- denominator is a power of 2. Otherwise, return 'Nothing'.+ maybe_dyadic :: a -> Maybe b++-- | Convert a \"rational\" value to a \"dyadic\" value, if the+-- denominator is a power of 2. Otherwise, throw an error.+to_dyadic :: (ToDyadic a b) => a -> b+to_dyadic a = case maybe_dyadic a of+ Just b -> b+ Nothing -> error "to_dyadic: denominator not a power of 2"++instance ToDyadic Dyadic Dyadic where+ maybe_dyadic = return++instance ToDyadic Rational Dyadic where+ maybe_dyadic x = do+ k <- log2 denom+ return (Dyadic numer k)+ where denom = denominator x+ numer = numerator x++instance ToDyadic Rationals Dyadic where+ maybe_dyadic = maybe_dyadic . unRationals++instance (ToDyadic a b) => ToDyadic (RootTwo a) (RootTwo b) where+ maybe_dyadic (RootTwo x y) = do+ x' <- maybe_dyadic x+ y' <- maybe_dyadic y+ return (RootTwo x' y')++instance (ToDyadic a b) => ToDyadic (Cplx a) (Cplx b) where+ maybe_dyadic (Cplx x y) = do+ x' <- maybe_dyadic x+ y' <- maybe_dyadic y+ return (Cplx x' y')++instance (ToDyadic a b) => ToDyadic (Omega a) (Omega b) where+ maybe_dyadic (Omega x y z w) = do+ x' <- maybe_dyadic x+ y' <- maybe_dyadic y+ z' <- maybe_dyadic z+ w' <- maybe_dyadic w+ return (Omega x' y' z' w')++-- ----------------------------------------------------------------------+-- * Real part+ +-- | A type class for rings that have a \"real\" component. A typical+-- instance is /a/ = 'DRComplex' with /b/ = 'DRootTwo'.+class RealPart a b | a -> b where+ -- | Take the real part.+ real :: a -> b++instance RealPart (Cplx a) a where+ real (Cplx a b) = a++instance (HalfRing a) => RealPart (Omega a) (RootTwo a) where+ real (Omega a b c d) = RootTwo d (half * (c - a))++-- ----------------------------------------------------------------------+-- * Rings of integers+ +-- | A type class for rings that have a distinguished subring \"of+-- integers\". A typical instance is /a/ = 'DRootTwo', which has /b/ =+-- 'ZRootTwo' as its ring of integers.+class WholePart a b | a -> b where + -- | The embedding of the ring of integers into the larger ring.+ from_whole :: b -> a+ -- | The inverse of 'from_whole'. Throws an error if the given+ -- element is not actually an integer in the ring.+ to_whole :: a -> b+ +instance WholePart Dyadic Integer where+ from_whole = fromInteger+ to_whole d + | n == 0 = a+ | otherwise = error "to_whole: non-integral value"+ where+ (a,n) = decompose_dyadic d++instance WholePart DRootTwo ZRootTwo where+ from_whole = fromZRootTwo+ to_whole (RootTwo x y) = RootTwo (to_whole x) (to_whole y)+ +instance WholePart DOmega ZOmega where+ from_whole = fromZOmega+ to_whole (Omega x y z w) = Omega (to_whole x) (to_whole y) (to_whole z) (to_whole w)+ +instance (WholePart a a', WholePart b b') => WholePart (a,b) (a',b') where+ from_whole (x,y) = (from_whole x, from_whole y)+ to_whole (x,y) = (to_whole x, to_whole y)+ +instance WholePart () () where + from_whole = const ()+ to_whole = const ()+ +instance (WholePart a b) => WholePart [a] [b] where + from_whole = map from_whole+ to_whole = map to_whole+ +instance (WholePart a b) => WholePart (Cplx a) (Cplx b) where + from_whole (Cplx a b) = Cplx (from_whole a) (from_whole b)+ to_whole (Cplx a b) = Cplx (to_whole a) (to_whole b)+ +-- ----------------------------------------------------------------------+-- * Common denominators+ +-- | A type class for things from which a common power of 1\/√2 (a+-- least denominator exponent) can be factored out. Typical instances+-- are 'DRootTwo', 'DRComplex', as well as tuples, lists, vectors, and+-- matrices thereof.+class DenomExp a where+ -- | Calculate the least denominator exponent /k/ of /a/. Returns+ -- the smallest /k/≥0 such that /a/ = /b/\/√2[sup /k/] for some+ -- integral /b/.+ denomexp :: a -> Integer+ + -- | Factor out a /k/th power of 1\/√2 from /a/. In other words,+ -- calculate /a/√2[sup /k/].+ denomexp_factor :: a -> Integer -> a++-- | Calculate and factor out the least denominator exponent /k/ of+-- /a/. Return (/b/,/k/), where /a/ = /b/\/(√2)[sup /k/] and /k/≥0.+denomexp_decompose :: (WholePart a b, DenomExp a) => a -> (b, Integer)+denomexp_decompose a = (b, k) where+ k = denomexp a+ b = to_whole (denomexp_factor a k)++-- | Generic 'show'-like method that factors out a common denominator+-- exponent.+showsPrec_DenomExp :: (WholePart a b, Show b, DenomExp a) => Int -> a -> ShowS+showsPrec_DenomExp d a + | k == 0 = showsPrec d b+ | k == 1 = showParen (d >= 8) $ + showString "roothalf * " . showsPrec 7 b+ | otherwise = showParen (d >= 8) $+ showString ("roothalf^" ++ show k ++ " * ") . showsPrec 7 b+ where (b, k) = denomexp_decompose a++instance DenomExp DRootTwo where+ denomexp (RootTwo x y) = k'+ where+ (a,k) = decompose_dyadic x+ (b,l) = decompose_dyadic y+ k' = maximum [2*k, 2*l-1]+ denomexp_factor a k = a * roottwo^k++instance DenomExp DOmega where+ denomexp (Omega x y z w) = k'+ where+ (a,ak) = decompose_dyadic x+ (b,bk) = decompose_dyadic y+ (c,ck) = decompose_dyadic z+ (d,dk) = decompose_dyadic w+ k = maximum [ak, bk, ck, dk]+ a' = if k == ak then a else 0+ b' = if k == bk then b else 0+ c' = if k == ck then c else 0+ d' = if k == dk then d else 0+ k' | k>0 && even (a'-c') && even (b'-d') = 2*k-1+ | otherwise = 2*k+ denomexp_factor a k = a * roottwo^k+ +instance (DenomExp a, DenomExp b) => DenomExp (a,b) where+ denomexp (a,b) = denomexp a `max` denomexp b+ denomexp_factor (a,b) k = (denomexp_factor a k, denomexp_factor b k)++instance DenomExp () where+ denomexp _ = 0+ denomexp_factor _ k = ()++instance (DenomExp a) => DenomExp [a] where+ denomexp as = maximum (0 : [ denomexp a | a <- as ])+ denomexp_factor as k = [ denomexp_factor a k | a <- as ]++instance (DenomExp a) => DenomExp (Cplx a) where+ denomexp (Cplx a b) = denomexp a `max` denomexp b+ denomexp_factor (Cplx a b) k = Cplx (denomexp_factor a k) (denomexp_factor b k)++-- ----------------------------------------------------------------------+-- * Conversion to ℚ[ω]++-- $ 'QOmega' is the largest one of our \"exact\" arithmetic types. We+-- define a 'toQOmega' family of functions for converting just about+-- anything to 'QOmega'.++-- | A type class for things that can be exactly converted to ℚ[ω].+class ToQOmega a where+ -- | Conversion to 'QOmega'.+ toQOmega :: a -> QOmega++instance ToQOmega Integer where+ toQOmega = fromInteger++instance ToQOmega Rational where+ toQOmega = fromRational++instance (ToQOmega a) => ToQOmega (RootTwo a) where+ toQOmega (RootTwo a b) = toQOmega a + roottwo * toQOmega b+ +instance ToQOmega Dyadic where+ toQOmega (Dyadic a n)+ | n >= 0 = toQOmega a * half^n+ | otherwise = toQOmega a * 2^(-n)++instance (ToQOmega a) => ToQOmega (Cplx a) where+ toQOmega (Cplx a b) = toQOmega a + i * toQOmega b++instance (ToQOmega a) => ToQOmega (Omega a) where+ toQOmega (Omega a b c d) = omega^3 * a' + omega^2 * b' + omega * c' + d'+ where+ a' = toQOmega a+ b' = toQOmega b+ c' = toQOmega c+ d' = toQOmega d++-- ----------------------------------------------------------------------+-- * Parity+ +-- | A type class for things that have parity.+class Parity a where+ -- | Return the parity of something.+ parity :: a -> Z2++instance Integral a => Parity a where+ parity x = if even x then 0 else 1+ +instance Parity ZRootTwo where+ parity (RootTwo a b) = parity a++-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | If /n/≠0, return (/a/,/k/) such that /a/ is odd and /n/ =+-- /a/⋅2[sup /k/]. If /n/=0, return (/0/,/0/).+lobit :: Integer -> (Integer, Integer)+lobit 0 = (0,0)+lobit n = aux n 0 where+ aux n !k+ | n .&. 0xffffffff == 0 = aux (shiftR n 32) (k+32)+ | n .&. 0xff == 0 = aux (shiftR n 8) (k+8)+ | even n = aux (shiftR n 1) (k+1)+ | otherwise = (n,k)+ +-- | If /n/ is of the form 2[sup /k/], return /k/. Otherwise, return+-- 'Nothing'.+log2 :: Integer -> Maybe Integer+log2 n+ | a == 1 = Just k+ | otherwise = Nothing+ where+ (a,k) = lobit n++-- | For /n/ ≥ 0, return the floor of the square root of /n/. This is+-- done using integer arithmetic, so there are no rounding errors.+intsqrt :: (Integral n) => n -> n+intsqrt n + | n <= 0 = 0+ | otherwise = iterate 1 + where+ iterate m+ | m_sq <= n && m_sq + 2*m + 1 > n = m+ | otherwise = iterate ((m + n `div` m) `div` 2)+ where+ m_sq = m*m+
+ Quantum/Synthesis/Ring/FixedPrec.hs view
@@ -0,0 +1,26 @@+-- | This module provides ring instances for "Data.Number.FixedPrec".++module Quantum.Synthesis.Ring.FixedPrec where++import Quantum.Synthesis.Ring++import Data.Number.FixedPrec++instance Precision e => RootHalfRing (FixedPrec e) where+ roothalf = sqrt 0.5++instance Precision e => RootTwoRing (FixedPrec e) where+ roottwo = sqrt 2++instance Precision e => HalfRing (FixedPrec e) where+ half = 0.5++instance Precision e => Adjoint (FixedPrec e) where+ adj x = x+ +instance Precision e => Adjoint2 (FixedPrec e) where+ adj2 x = x++instance Precision e => Floor (FixedPrec e) where+ floor_of = floor+ ceiling_of = ceiling
+ Quantum/Synthesis/Ring/SymReal.hs view
@@ -0,0 +1,21 @@+-- | This module provides ring instances for "Quantum.Synthesis.SymReal".++module Quantum.Synthesis.Ring.SymReal where++import Quantum.Synthesis.Ring+import Quantum.Synthesis.SymReal++instance RootHalfRing SymReal where+ roothalf = sqrt 0.5++instance RootTwoRing SymReal where+ roottwo = sqrt 2++instance HalfRing SymReal where+ half = 0.5++instance Adjoint SymReal where+ adj x = x+ +instance Adjoint2 SymReal where+ adj2 x = x
+ Quantum/Synthesis/RotationDecomposition.hs view
@@ -0,0 +1,160 @@+-- | This module provides functions for decomposing a unitary /n/×/n/+-- operator into one- and two-level unitaries. +-- +-- The algorithm is adapted from Section 4.5.1 of Nielsen and+-- Chuang. In addition to what is described in Nielsen and Chuang, our+-- algorithm produces two-level operators that can be decomposed using+-- only two Euler angles. The algorithm produces at most /n/(/n/−1)\/2+-- two-level operators of type /R/[sub /z/](δ)/R/[sub /x/](γ), as well+-- as /n/ one-level operators of type [exp /i/θ]. Therefore, the+-- decomposition of a unitary /n/×/n/ operator yields /n/[sup 2] real+-- parameters, which is optimal.++module Quantum.Synthesis.RotationDecomposition where++import Quantum.Synthesis.Matrix+import Quantum.Synthesis.MultiQubitSynthesis+import Quantum.Synthesis.Ring+import Quantum.Synthesis.EulerAngles+import Quantum.Synthesis.ArcTan2++import Data.List+import System.Random++-- ----------------------------------------------------------------------+-- * Elementary rotations++-- | An elementary rotation is either a combined /x/- and+-- /z/-rotation, applied at indices /j/ and /k/, or a phase change+-- applied at index /j/.+-- +-- * 'ERot_zx' δ γ /j/ /k/ represents the operator +-- /R/[sub /z/](δ)/R/[sub /x/](γ), applied to levels /j/ and /k/.+-- +-- \[image ERot_zx.png]+-- +-- * 'ERot_phase' θ /j/ represents the operator [exp /i/θ] applied to level+-- /j/.+-- +-- \[image ERot_phase.png]+-- +-- Note: when we use a list of 'ElementaryRot's to express a sequence of+-- operators, the operators are meant to be applied right-to-left,+-- i.e., as in the mathematical notation for matrix multiplication.+-- This is the opposite of the quantum circuit notation.+data ElementaryRot a = + ERot_zx a a Index Index+ | ERot_phase a Index+ deriving (Show)++-- | Convert a symbolic elementary rotation to a concrete matrix.+matrix_of_elementary :: (Ring a, Floating a, Nat n) => ElementaryRot a -> Matrix n n (Cplx a)+matrix_of_elementary (ERot_zx delta gamma j k) = + twolevel_matrix (a, b) (c, d) j k where+ a = ed' * cg+ b = -i * ed' * sg+ c = -i * ed * sg+ d = ed * cg+ cg = Cplx (cos (gamma/2)) 0+ sg = Cplx (sin (gamma/2)) 0+ ed = Cplx cd sd+ ed' = Cplx cd (-sd)+ cd = cos (delta/2) + sd = sin (delta/2)+matrix_of_elementary (ERot_phase theta j) = + onelevel_matrix (Cplx c s) j where+ c = cos theta+ s = sin theta++-- | Convert a sequence of elementary rotations to an /n/×/n/-matrix.+matrix_of_elementaries :: (Ring a, Floating a, Nat n) => [ElementaryRot a] -> Matrix n n (Cplx a)+matrix_of_elementaries ops =+ foldl' (*) 1 [ matrix_of_elementary op | op <- ops ]++-- ----------------------------------------------------------------------+-- * Decomposition into elementary rotations++-- | Convert an /n/×/n/-matrix to a sequence of elementary rotations.+-- +-- Note: the list of elementary rotations will be returned in+-- right-to-left order, i.e., as in the mathematical notation for+-- matrix multiplication. This is the opposite of the quantum circuit+-- notation.+rotation_decomposition :: (Eq a, Fractional a, Floating a, Adjoint a, ArcTan2 a, Nat n) => Matrix n n (Cplx a) -> [ElementaryRot a]+rotation_decomposition op = concat gates ++ reverse gates' where+ (op', gates) = mapAccumL rowop op [ (i,j) | j <- [0..n-2], i <- [j+1..n-1] ]+ gates' = [ get_phase op' i | i <- [0..n-1] ]+ (n', _) = matrix_size op+ n = fromInteger n'++-- ----------------------------------------------------------------------+-- * Auxiliary functions++-- | Construct a two-level /n/×/n/-matrix from a given 2×2-matrix and+-- indices /j/ and /k/.+twolevel_matrix_of_matrix :: (Ring a, Nat n) => Matrix Two Two a -> Index -> Index -> Matrix n n a+twolevel_matrix_of_matrix u j k = op where+ op = twolevel_matrix (a,b) (c,d) j k+ ((a,b), (c,d)) = from_matrix2x2 u+ +-- | Extract the phase of the /j/th diagonal entry of the given+-- matrix.+get_phase :: (ArcTan2 a) => Matrix n n (Cplx a) -> Index -> ElementaryRot a+get_phase op j = ERot_phase theta j where+ a = matrix_index op j j+ theta = arctan2 y x+ Cplx x y = a+ +-- | Perform a two-level operation on rows /j/ and /k/ of a matrix /U/,+-- such that the resulting matrix has a 0 in the (/j/,/k/)-position.+-- Return the inverse of the two-level operation used, as well as the+-- updated matrix.+rowop :: (Eq a, Fractional a, Floating a, Adjoint a, ArcTan2 a, Nat n) => Matrix n n (Cplx a) -> (Index, Index) -> (Matrix n n (Cplx a), [ElementaryRot a])+rowop op (j,k) + | b == 0 = (op, [])+ | otherwise = (op', gates) + where+ a = matrix_index op k k+ b = matrix_index op j k+ matrix = 1/Cplx (sqrt(real (a * adj a + b * adj b))) 0 `scalarmult` matrix2x2 (adj a, adj b) (b, -a)+ (alpha, beta, gamma, delta) = euler_angles matrix+ matrix2 = matrix_of_euler_angles (0, 0, gamma, delta)+ op' = twolevel_matrix_of_matrix matrix2 k j .*. op+ gates = [ ERot_zx (-delta) (-gamma) k j ]++-- ----------------------------------------------------------------------+-- * Testing++-- | Return a \"random\" unitary /n/×/n/-matrix. These matrices will+-- not quite be uniformly distributed; this function is primarily+-- meant to generate test cases. +random_unitary :: (RandomGen g, Nat n, Floating a, Random a) => g -> Matrix n n (Cplx a)+random_unitary g = op where+ op = matrix_of_elementaries gates+ gates = random_gates g (20*n^2)+ random_gates g 0 = []+ random_gates g m = h:t where+ (gamma, g1) = randomR (0, 2*pi) g+ (delta, g1') = randomR (0, 2*pi) g1+ (c, g2) = randomR (0, 1) g1'+ (j, g3) = randomR (0, n-2) g2+ (k, g4) = randomR (j+1, n-1) g3+ h = case c :: Int of+ 0 -> ERot_zx delta gamma j k+ _ -> ERot_phase delta j+ t = random_gates g4 (m-1)+ (n', _) = matrix_size op+ n = fromInteger n'++-- | Generate a random matrix, decompose it, and then re-calculate the+-- matrix from the decomposition.+test :: IO ()+test = do+ g <- newStdGen+ let m = random_unitary g :: Matrix Four Four CDouble+ let gates = rotation_decomposition m+ let m' = matrix_of_elementaries gates :: Matrix Four Four CDouble+ putStrLn $ "m = " ++ show m+ putStrLn $ "gates = " ++ show gates+ putStrLn $ "m' = " ++ show m'+
+ Quantum/Synthesis/SymReal.hs view
@@ -0,0 +1,493 @@+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Rank2Types #-}++-- | This module provides a symbolic representation of real number+-- expressions, as well as a type class of things that can be+-- converted to arbitrary precision real numbers.+module Quantum.Synthesis.SymReal where++import Quantum.Synthesis.ArcTan2++import Control.Monad+import Data.Char (isAlpha, isAlphaNum, isDigit)+import Data.Number.FixedPrec+import Text.ParserCombinators.ReadP+import Data.Ratio++-- ----------------------------------------------------------------------+-- * Symbolic real number expressions++-- | A type to represent symbolic expressions for real numbers.+-- +-- Caution: equality '==' at this type denotes symbolic equality of+-- expressions, not equality of the defined real numbers.+data SymReal =+ Const Integer -- ^ An integer constant.+ | Decimal Rational String -- ^ A decimal constant. This has a rational value and a string representation.+ | Plus SymReal SymReal -- ^ /x/ @+@ /y/.+ | Minus SymReal SymReal -- ^ /x/ @−@ /y/.+ | Times SymReal SymReal -- ^ /x/ @*@ /y/.+ | Div SymReal SymReal -- ^ /x/ @\/@ /y/.+ | Negate SymReal -- ^ \−/x/.+ | Abs SymReal -- ^ |/x/|.+ | Signum SymReal -- ^ signum(/x/).+ | Recip SymReal -- ^ 1\//x/.+ | Pi -- ^ π.+ | Euler -- ^ /e/.+ | Exp SymReal -- ^ \[exp /x/].+ | Sqrt SymReal -- ^ √/x/.+ | Log SymReal -- ^ log /x/.+ | Power SymReal SymReal -- ^ /x/[sup /y/].+ | Sin SymReal -- ^ sin /x/.+ | Tan SymReal -- ^ cos /x/.+ | Cos SymReal -- ^ cos /x/.+ | ASin SymReal -- ^ asin /x/.+ | ATan SymReal -- ^ atan /x/.+ | ACos SymReal -- ^ acos /x/.+ | Sinh SymReal -- ^ sinh /x/.+ | Tanh SymReal -- ^ tanh /x/.+ | Cosh SymReal -- ^ cosh /x/.+ | ASinh SymReal -- ^ asinh /x/.+ | ATanh SymReal -- ^ atanh /x/.+ | ACosh SymReal -- ^ acosh /x/.+ | ArcTan2 SymReal SymReal -- ^ arctan2 /x/ /y/.+ deriving (Eq)++instance Show SymReal where+ showsPrec d (Const x) = showsPrec d x+ showsPrec d (Decimal x s) = showString s+ showsPrec d (Plus x y) = showParen (d > 6) $ showsPrec 6 x . showString "+" . showsPrec 6 y+ showsPrec d (Minus x y) = showParen (d > 6) $ showsPrec 6 x . showString "-" . showsPrec 7 y+ showsPrec d (Times x y) = showParen (d > 7) $ showsPrec 7 x . showString "*" . showsPrec 7 y+ showsPrec d (Div x y) = showParen (d > 7) $ showsPrec 7 x . showString "/" . showsPrec 8 y+ showsPrec d (Power x y) = showParen (d > 8) $ showsPrec 9 x . showString "**" . showsPrec 9 y+ showsPrec d (Negate x) = showParen (d > 5) $ showString "-" . showsPrec 7 x+ showsPrec d (Abs x) = showParen (d > 10) $ showString "abs " . showsPrec 11 x+ showsPrec d (Signum x) = showParen (d > 10) $ showString "signum " . showsPrec 11 x+ showsPrec d (Recip x) = showParen (d > 7) $ showString "1/" . showsPrec 8 x+ showsPrec d Pi = showString "pi"+ showsPrec d Euler = showString "e"+ showsPrec d (Exp x) = showParen (d > 10) $ showString "exp " . showsPrec 11 x+ showsPrec d (Sqrt x) = showParen (d > 10) $ showString "sqrt " . showsPrec 11 x+ showsPrec d (Log x) = showParen (d > 10) $ showString "log " . showsPrec 11 x+ showsPrec d (Sin x) = showParen (d > 10) $ showString "sin " . showsPrec 11 x+ showsPrec d (Tan x) = showParen (d > 10) $ showString "tan " . showsPrec 11 x+ showsPrec d (Cos x) = showParen (d > 10) $ showString "cos " . showsPrec 11 x+ showsPrec d (ASin x) = showParen (d > 10) $ showString "asin " . showsPrec 11 x+ showsPrec d (ATan x) = showParen (d > 10) $ showString "atan " . showsPrec 11 x+ showsPrec d (ACos x) = showParen (d > 10) $ showString "acos " . showsPrec 11 x+ showsPrec d (Sinh x) = showParen (d > 10) $ showString "sinh " . showsPrec 11 x+ showsPrec d (Tanh x) = showParen (d > 10) $ showString "tanh " . showsPrec 11 x+ showsPrec d (Cosh x) = showParen (d > 10) $ showString "cosh " . showsPrec 11 x+ showsPrec d (ASinh x) = showParen (d > 10) $ showString "asinh " . showsPrec 11 x+ showsPrec d (ATanh x) = showParen (d > 10) $ showString "atanh " . showsPrec 11 x+ showsPrec d (ACosh x) = showParen (d > 10) $ showString "acosh " . showsPrec 11 x+ showsPrec d (ArcTan2 y x) = showParen (d > 10) $ showString "arctan2 " . showsPrec 11 y . showString " " . showsPrec 11 x++instance Num SymReal where+ (+) = Plus+ (*) = Times+ (-) = Minus+ negate = Negate+ abs = Abs+ signum = Signum+ fromInteger = Const+ +instance Fractional SymReal where+ (/) = Div+ recip = Recip+ fromRational x = Const (numerator x) `Div` Const (denominator x)+ +instance Floating SymReal where+ pi = Pi+ exp = Exp+ sqrt = Sqrt+ log = Log+ (**) = Power+ logBase x y = log y / log x+ sin = Sin+ tan = Tan+ cos = Cos+ asin = ASin+ atan = ATan+ acos = ACos+ sinh = Sinh+ tanh = Tanh+ cosh = Cosh+ asinh = ASinh+ atanh = ATanh+ acosh = ACosh++instance ArcTan2 SymReal where+ arctan2 y x = ArcTan2 y x++-- ----------------------------------------------------------------------+-- * Conversion to real number types++-- | A type class for things that can be converted to a real number at+-- arbitrary precision.+class ToReal a where+ to_real :: (Floating r, ArcTan2 r) => a -> r++instance ToReal SymReal where+ to_real (Const x) = fromInteger x+ to_real (Decimal x s) = fromRational x+ to_real (Plus x y) = to_real x + to_real y+ to_real (Minus x y) = to_real x - to_real y+ to_real (Times x y) = to_real x * to_real y+ to_real (Negate x) = -(to_real x)+ to_real (Abs x) = abs (to_real x)+ to_real (Signum x) = signum (to_real x)+ to_real (Div x y) = to_real x / to_real y+ to_real (Recip x) = recip (to_real x)+ to_real Pi = pi+ to_real Euler = exp 1+ to_real (Exp x) = exp (to_real x)+ to_real (Sqrt x) = sqrt (to_real x)+ to_real (Log x) = log (to_real x)+ to_real (Power x y) = to_real x ** to_real y+ to_real (Sin x) = sin (to_real x)+ to_real (Tan x) = tan (to_real x)+ to_real (Cos x) = cos (to_real x)+ to_real (ASin x) = asin (to_real x)+ to_real (ATan x) = atan (to_real x)+ to_real (ACos x) = acos (to_real x)+ to_real (Sinh x) = sinh (to_real x)+ to_real (Tanh x) = tanh (to_real x)+ to_real (Cosh x) = cosh (to_real x)+ to_real (ASinh x) = asinh (to_real x)+ to_real (ATanh x) = atanh (to_real x)+ to_real (ACosh x) = acosh (to_real x)+ to_real (ArcTan2 y x) = arctan2 (to_real y) (to_real x)+ +instance ToReal Rational where+ to_real = fromRational+ +instance ToReal Integer where+ to_real = fromInteger+ +instance ToReal Int where+ to_real = fromIntegral+ +instance ToReal Double where+ to_real = fromRational . toRational++instance ToReal Float where+ to_real = fromRational . toRational++instance (Precision e) => ToReal (FixedPrec e) where+ to_real = fromRational . toRational++instance ToReal String where+ to_real x = case parse_SymReal x of+ Just n -> to_real n+ Nothing -> error "ToReal String: string does not parse"++-- ----------------------------------------------------------------------+-- ** Dynamic conversion to FixedPrec++-- | It would be useful to have a function for converting a symbolic+-- real number to a fixed-precision real number with a chosen+-- precision, such that the precision /e/ depends on a parameter /d/:+-- +-- > to_fixedprec :: (ToReal r) => Integer -> r -> FixedPrec e+-- > to_fixedprec d x = ...+-- +-- However, since /e/ is a type, /d/ is a term, and Haskell is not+-- dependently typed, this cannot be done directly.+-- +-- The function 'dynamic_fixedprec' is the closest thing we have to a+-- workaround. The call @dynamic_fixedprec@ /d/ /f/ /x/ calls+-- /f/(/x/'), where /x/' is the value /x/ converted to /d/ digits of+-- precision. In other words, we have+-- +-- > dynamic_fixedprec d f x = f (to_fixedprec d x),+-- +-- with the restriction that the precision /e/ cannot occur freely in+-- the result type of /f/.+dynamic_fixedprec :: forall a r.(ToReal r) => Integer -> (forall e.(Precision e) => FixedPrec e -> a) -> r -> a+dynamic_fixedprec d f x = loop d (undefined :: P0)+ where + loop :: forall e.(Precision e) => Integer -> e -> a+ loop d e+ | d >= 1000 = loop (d-1000) (undefined :: PPlus1000 e)+ | d >= 100 = loop (d-100) (undefined :: PPlus100 e)+ | d >= 10 = loop (d-10) (undefined :: PPlus10 e)+ | d > 0 = loop (d-1) (undefined :: PPlus1 e)+ | otherwise = f (to_real x :: FixedPrec e)++-- | Like 'dynamic_fixedprec', but take two real number arguments. In+-- terms of the fictitious function @to_fixedprec@, we have:+-- +-- > dynamic_fixedprec2 d f x y = f (to_fixedprec d x) (to_fixedprec d y).+dynamic_fixedprec2 :: forall a r s.(ToReal r, ToReal s) => Integer -> (forall e.(Precision e) => FixedPrec e -> FixedPrec e -> a) -> r -> s -> a+dynamic_fixedprec2 d f x y = loop d (undefined :: P0)+ where + loop :: forall e.(Precision e) => Integer -> e -> a+ loop d e+ | d >= 1000 = loop (d-1000) (undefined :: PPlus1000 e)+ | d >= 100 = loop (d-100) (undefined :: PPlus100 e)+ | d >= 10 = loop (d-10) (undefined :: PPlus10 e)+ | d > 0 = loop (d-1) (undefined :: PPlus1 e)+ | otherwise = f (to_real x :: FixedPrec e) (to_real y :: FixedPrec e)++-- ----------------------------------------------------------------------+-- * A parser for real number expressions+ +-- ----------------------------------------------------------------------+-- ** Grammar specification++-- $ Each function in this section corresponds to a production rule+-- for a context-free grammar. The type of each function is 'ReadP'+-- /a/, where /a/ is the type of the semantic value produced by the+-- grammar for that expression.+-- +-- The parser uses simple precedences. +-- +-- * Unary \"+\" and \"−\" have precedence 6. +-- +-- * Binary \"+\" and \"−\" have precedence 6 and are left+-- associative.+-- +-- * Binary \"*\" and \"\/\" have precedence 7 and are left+-- associative.+-- +-- * Binary \"**\" and \"^\" have precedence 8 and are right+-- associative.+-- +-- * All unary operators other than \"+\" and \"−\" have precedence+-- 10.+-- +-- We use /exp6/ to denote an expression whose+-- top-level operator has precedence 6 or higher, /exp7/ to denote an+-- expression whose top-level operator has precedence 7 or higher, and+-- so on.+-- +-- We also allow whitespace between lexicographic entities. For+-- simplicity, whitespace is not shown in the production rules,+-- although it appears in the code.++-- | /integer/ ::= /digit/ /digit/*.+integer :: ReadP SymReal+integer = do+ s <- munch1 isDigit+ let n = read s+ return (Const (fromInteger n))++-- | /float/ ::= /digit/* \".\" /digit/*.+-- +-- There must be at least one digit, either before or after the decimal point.+float :: ReadP SymReal+float = do+ (s1, _) <- gather $ do+ munch isDigit+ char '.'+ (s2, _) <- gather $ do+ munch isDigit+ when (length s1 == 0 && length s2 == 0) $ do+ pfail+ let num = read (s1++s2) :: Integer+ let denom = 10^(length s2)+ let s1' = if s1 == [] then "0" else s1+ let s2' = if s2 == [] then "0" else s2+ return (Decimal (num % denom) (s1' ++ "." ++ s2'))++-- | /const_pi/ ::= \"pi\".+const_pi :: ReadP SymReal+const_pi = do+ string "pi"+ return Pi++-- | /const_e/ ::= \"e\".+const_e :: ReadP SymReal+const_e = do+ string "e"+ return Euler++-- | /negative/ ::= \"−\".+negative :: ReadP (SymReal -> SymReal)+negative = do+ string "-"+ skipSpaces+ return Negate++-- | /positive/ ::= \"+\".+positive :: ReadP (SymReal -> SymReal)+positive = do+ string "+"+ skipSpaces+ return id++-- | /plus_term/ ::= \"+\" /exp7/.+plus_term :: ReadP (SymReal -> SymReal)+plus_term = do+ skipSpaces+ string "+"+ skipSpaces+ n2 <- exp7+ return (\n1 -> Plus n1 n2)++-- | /minus_term/ ::= \"−\" /exp7/.+minus_term :: ReadP (SymReal -> SymReal)+minus_term = do+ skipSpaces+ string "-"+ skipSpaces+ n2 <- exp7+ return (\n1 -> Minus n1 n2)++-- | /times_term/ ::= \"*\" /exp8/.+times_term :: ReadP (SymReal -> SymReal)+times_term = do+ skipSpaces+ string "*"+ skipSpaces+ n2 <- exp8+ return (\n1 -> Times n1 n2)++-- | /div_term/ ::= \"\/\" /exp8/.+div_term :: ReadP (SymReal -> SymReal)+div_term = do+ skipSpaces+ string "/"+ skipSpaces+ n2 <- exp8+ return (\n1 -> Div n1 n2)++-- | /power_term/ ::= /exp10/ \"**\" | /exp10/ \"^\".+power_term :: ReadP (SymReal -> SymReal)+power_term = do+ n1 <- exp10+ skipSpaces+ string "**" +++ string "^"+ skipSpaces+ return (\n2 -> Power n1 n2)++-- | /unary_fun/ ::= /unary_op/ /exp10/.+unary_fun :: ReadP SymReal+unary_fun = do+ skipSpaces+ op <- unary_op+ skipSpaces+ n <- exp10+ return (op n)++-- | /unary_op/ ::= \"abs\" | \"signum\" | ...+unary_op :: ReadP (SymReal -> SymReal)+unary_op = + choice [ do { string s; return op } | (s, op) <- ops ]+ where + ops = [ ("abs", Abs),+ ("signum", Signum),+ ("recip", Recip),+ ("exp", Exp),+ ("sqrt", Sqrt),+ ("log", Log),+ ("sin", Sin),+ ("tan", Tan),+ ("cos", Cos),+ ("asin", ASin),+ ("atan", ATan),+ ("acos", ACos),+ ("sinh", Sinh),+ ("tanh", Tanh),+ ("cosh", Cosh),+ ("asinh", ASinh),+ ("atanh", ATanh),+ ("acosh", ACosh) ]++-- | /binary_fun/ ::= /binary_op/ /exp10/ /exp10/.+binary_fun :: ReadP SymReal+binary_fun = do+ skipSpaces+ op <- binary_op+ skipSpaces+ n <- exp10+ skipSpaces+ m <- exp10+ return (op n m)++-- | /binary_op/ ::= \"abs\" | \"signum\" | ...+binary_op :: ReadP (SymReal -> SymReal -> SymReal)+binary_op = + choice [ do { string s; return op } | (s, op) <- ops ]+ where + ops = [ ("arctan2", ArcTan2) ]++-- | /exp6/ ::= (/negative/ | /positive/)? /exp7/ ( /plus_term/ | /minus_term/ )*.+-- +-- An expression whose top-level operator has precedence 6 or+-- above. The operators of precedence 6 are \"+\" and \"−\".+exp6 :: ReadP SymReal+exp6 = do+ sign <- option id (negative +++ positive)+ n1 <- exp7+ ops <- many $ do+ plus_term +++ minus_term+ return (foldl (\x f -> f x) (sign n1) ops)++-- | /exp7/ ::= /exp8/ ( /times_term/ | /div_term/ )*.+-- +-- An expression whose top-level operator has precedence 7 or+-- above. The operators of precedence 6 are \"*\" and \"\/\".+exp7 :: ReadP SymReal+exp7 = do+ n1 <- exp8+ ops <- many $ do+ times_term +++ div_term+ return (foldl (\x f -> f x) n1 ops)++-- | /exp8/ ::= ( /power_term/ )* /exp10/+-- +-- An expression whose top-level operator has precedence 8 or+-- above. The operators of precedence 6 are \"**\" and \"^\".+exp8 :: ReadP SymReal+exp8 = do+ ops <- many $ do+ power_term+ n2 <- exp10+ return (foldr (\f x -> f x) n2 ops)++-- | /exp10/ ::= /parenthesized/ | /const_pi/ | /const_e/ | /integer/ | /float/ | /unary_fun/ | /binary_fun/.+-- +-- An expression whose top-level operator has precedence 10 or+-- above. Such expressions are constants, applications of unary+-- operators (except unary \"−\" and \"+\"), and parenthesized+-- expressions.+exp10 :: ReadP SymReal+exp10 = parenthesized +++ const_pi +++ const_e +++ integer +++ float +++ unary_fun +++ binary_fun++-- | /parenthesized/ ::= \"(\" /exp6/ \")\".+parenthesized :: ReadP SymReal+parenthesized = do+ string "("+ skipSpaces+ n <- exp6+ skipSpaces+ string ")"+ return n++-- | /expression/ ::= /exp6/ /end-of-line/.+-- +-- This is a top-level expression.+expression :: ReadP SymReal+expression = do+ skipSpaces+ s <- exp6+ skipSpaces+ eof+ return s++-- ----------------------------------------------------------------------+-- ** Top-level parser++-- | Parse a symbolic real number expression. Typical strings that can+-- be parsed are @\"1.0\"@, @\"pi\/128\"@, @\"(1+sin(pi\/3))^2\"@, etc. If+-- the expression cannot be parsed, return 'Nothing'.+parse_SymReal :: String -> Maybe SymReal+parse_SymReal s =+ case readP_to_S expression s of+ (h, ""):_ -> Just h+ _ -> Nothing
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Superdoc+main = superdocMain
+ images/E.png view
binary file changed (absent → 1496 bytes)
+ images/ERot_phase.png view
binary file changed (absent → 1572 bytes)
+ images/ERot_zx.png view
binary file changed (absent → 3957 bytes)
+ newsynth.cabal view
@@ -0,0 +1,102 @@+-- The name of the package.+name: newsynth++-- The package version. See the Haskell package versioning policy (PVP) +-- for standards guiding when and how versions should be incremented.+-- http://www.haskell.org/haskellwiki/Package_versioning_policy+-- PVP summary: +-+------- breaking API changes+-- | | +----- non-breaking API additions+-- | | | +--- code changes with no API change+version: 0.1.0.0++-- A short (one-line) description of the package.+synopsis: Exact and approximate synthesis of quantum circuits++-- A longer description of the package.+description: ++ A library of algorithms for exact and approximate synthesis of+ quantum circuits over the Clifford+T gate set. This includes, among+ other things:+ .+ * "Quantum.Synthesis.Newsynth": an efficient single-qubit+ approximate synthesis algorithm. From P. Selinger, \"Efficient+ Clifford+T approximation of single-qubit operators\",+ <http://arxiv.org/abs/1212.6253>.+ .+ * "Quantum.Synthesis.MultiQubitSynthesis": multi-qubit exact+ synthesis algorithms. From B. Giles and P. Selinger, \"Exact+ synthesis of multiqubit Clifford+/T/ circuits\", Physical Review A+ 87, 032332, 2013, <http://arxiv.org/abs/1212.0506>.+ .+ * "Quantum.Synthesis.CliffordT": the computation of+ Matsumoto-Amano normal forms. From K. Matsumoto and K. Amano,+ \"Representation of Quantum Circuits with Clifford and π\/8+ Gates\", <http://arxiv.org/abs/0806.3834>.+ .+ * "Quantum.Synthesis.RotationDecomposition": an algorithm for+ decomposing multi-qubit unitary operators into one- and two-level+ unitaries. See e.g. Section 4.5.1 of M. A. Nielsen and+ I. L. Chuang, \"Quantum Computation and Quantum Information\",+ Cambridge University Press, 2002.+ .+ This package also provides an easy-to-use command line tool for+ single-qubit approximate synthesis.++-- URL for the project homepage or repository.+homepage: http://www.mathstat.dal.ca/~selinger/newsynth/++-- The license under which the package is released.+license: GPL-3++-- The file containing the license text.+license-file: LICENSE++-- The package author(s).+author: Peter Selinger++-- An email address to which users can send suggestions, bug reports, and +-- patches.+maintainer: selinger@mathstat.dal.ca++-- A copyright notice.+copyright: Copyright (c) 2012-2013 Peter Selinger++-- A classification category for future use by the package catalogue+-- Hackage. These categories have not yet been specified, but the+-- upper levels of the module hierarchy make a good start.+category: Quantum++-- The type of build used by this package.+build-type: Custom++-- Constraint on the version of Cabal needed to build this package.+cabal-version: >=1.8++-- A list of additional files to be included in source distributions+-- built with setup sdist.+extra-source-files: images/*.png ChangeLog++library+ -- Modules exported by the library.+ exposed-modules: Quantum.Synthesis.Newsynth, Quantum.Synthesis.Matrix, Quantum.Synthesis.LaTeX, Quantum.Synthesis.RotationDecomposition, Quantum.Synthesis.ArcTan2, Quantum.Synthesis.EulerAngles, Quantum.Synthesis.EuclideanDomain, Quantum.Synthesis.SymReal, Quantum.Synthesis.Ring, Quantum.Synthesis.Clifford, Quantum.Synthesis.MultiQubitSynthesis, Quantum.Synthesis.CliffordT, Quantum.Synthesis.Ring.FixedPrec, Quantum.Synthesis.Ring.SymReal+ + -- Modules included in this library but not exported.+ -- other-modules: + + -- Other library packages from which modules are imported.+ build-depends: base ==4.6.*, random ==1.0.*, fixedprec ==0.2.*, superdoc ==0.1.*+++executable newsynth+ -- .hs or .lhs file containing the Main module.+ main-is: newsynth.hs++ -- Root directories for the module hierarchy.+ hs-source-dirs: programs++ -- Modules included in this executable, other than Main.+ other-modules: CommandLine+ + -- Other library packages from which modules are imported.+ build-depends: base ==4.6.*, random ==1.0.*, time ==1.4.*, superdoc ==0.1.*, newsynth
+ programs/CommandLine.hs view
@@ -0,0 +1,81 @@+-- | This module provides some functions that are useful in the+-- processing of command line options, and that are shared between+-- several algorithms.++module CommandLine where++-- import other stuff+import System.Exit+import System.IO+import Data.List+import Data.Char++-- ----------------------------------------------------------------------+-- * Formatting of lists and strings++-- | A general list-to-string function. Example:+-- +-- > string_of_list "{" ", " "}" "{}" show [1,2,3] = "{1, 2, 3}"+string_of_list :: String -> String -> String -> String -> (t -> String) -> [t] -> String+string_of_list lpar comma rpar nil string_of_elt lst =+ let string_of_tail lst =+ case lst of+ [] -> ""+ h:t -> comma ++ string_of_elt h ++ string_of_tail t+ in+ case lst of+ [] -> nil+ h:t -> lpar ++ string_of_elt h ++ string_of_tail t ++ rpar++-- ----------------------------------------------------------------------+-- * Option processing+ +-- | Exit with an error message after a command line error. This also+-- outputs information on where to find command line help.+optfail :: String -> IO a+optfail msg = do+ hPutStr stderr msg+ hPutStrLn stderr "Try --help for more info."+ exitFailure++-- | Parse a string to an integer, or return 'Nothing' on failure.+parse_int :: (Integral r) => String -> Maybe r+parse_int s = case reads s of+ [(n, "")] -> Just (fromInteger n)+ _ -> Nothing++-- | Parse a string to a list of integers, or return 'Nothing' on failure.+parse_list_int :: String -> Maybe [Int] +parse_list_int s = case reads s of+ [(ns, "")] -> Just ns+ _ -> Nothing++-- | Parse a string to a 'Double', or return 'Nothing' on failure.+parse_double :: String -> Maybe Double+parse_double s = case reads s of+ [(n, "")] -> Just n+ _ -> Nothing++-- | In an association list, find the key that best matches the given+-- string. If one key matches exactly, return the corresponding+-- key-value pair. Otherwise, return a list of all key-value pairs+-- whose key have the given string as a prefix. This list could be of+-- length 0 (no match), 1 (unique match), or greater (ambiguous key).+-- Note: the keys in the association list must be lower case. The+-- input string is converted to lower case as well, resulting in+-- case-insensitive matching.+match_enum :: [(String, a)] -> String -> [(String, a)]+match_enum list key =+ case lookup s list of+ Just v -> [(s,v)]+ Nothing -> filter (\(k,v) -> isPrefixOf s k) list+ where+ s = map toLower key+ +-- | Pretty-print a list of possible values for a parameter. The+-- first argument is the name of the parameter, and the second+-- argument is its enumeration.+show_enum :: String -> [(String, a)] -> String +show_enum param list =+ "Possible values for " ++ param ++ " are: " +++ string_of_list "" ", " "" "no possible values" fst list ++ ".\n"
+ programs/newsynth.hs view
@@ -0,0 +1,270 @@+-- | This module provides a command line interface to the+-- decomposition library.++module Main where++import Quantum.Synthesis.Newsynth+import Quantum.Synthesis.SymReal+import Quantum.Synthesis.CliffordT+import Quantum.Synthesis.Ring+import Quantum.Synthesis.Matrix+import Quantum.Synthesis.LaTeX++import CommandLine++-- import other stuff+import Control.Monad+import Data.Time+import System.Console.GetOpt+import System.Environment +import System.Exit+import System.Random+import Text.Printf++-- ----------------------------------------------------------------------+-- * Option processing++-- | A data type to hold values set by command line options.+data Options = Options {+ opt_digits :: Double, -- ^ Requested precision in digits (default: 10).+ opt_theta :: SymReal, -- ^ Angle to approximate.+ opt_hex :: Bool, -- ^ Output operator in hex coding? (default: ASCII).+ opt_stats :: Bool, -- ^ Output statistics?+ opt_latex :: Bool, -- ^ Additional LaTeX output?+ opt_count :: Integer, -- ^ Repetition count for stats (default: 1).+ opt_rseed :: Maybe StdGen -- ^ An optional random seed.+} deriving Show++-- | The default options.+defaultOptions :: Options+defaultOptions = Options+ { opt_digits = 10,+ opt_theta = 0.0,+ opt_hex = False,+ opt_stats = False,+ opt_latex = False,+ opt_count = 1,+ opt_rseed = Nothing+ }++-- | The list of command line options, in the format required by 'getOpt'.+options :: [OptDescr (Options -> IO Options)]+options =+ [ Option ['h'] ["help"] (NoArg help) "print usage info and exit",+ Option ['d'] ["digits"] (ReqArg digits "<n>") "set precision in decimal digits (default: 10)",+ Option ['b'] ["bits"] (ReqArg bits "<n>") "set precision in bits",+ Option ['e'] ["epsilon"] (ReqArg epsilon "<n>") "set precision as epsilon (default: 1e-10)",+ Option ['x'] ["hex"] (NoArg hex) "output hexadecimal coding (default: ASCII)",+ Option ['s'] ["stats"] (NoArg stats) "output statistics",+ Option ['l'] ["latex"] (NoArg latex) "additional output in LaTeX format",+ Option ['c'] ["count"] (ReqArg count "<n>") "average statistics over <n> runs (default: 1)",+ Option ['r'] ["rseed"] (ReqArg rseed "\"<s>\"") "set optional random seed (default: random)"+ ]+ where+ help :: Options -> IO Options+ help o = do+ usage+ exitSuccess++ digits :: String -> Options -> IO Options+ digits string o =+ case parse_double string of+ Just n | n >= 0 -> return o { opt_digits = n }+ Just n -> optfail ("Number of digits must not be negative -- " ++ string ++ "\n")+ _ -> optfail ("Invalid digits -- " ++ string ++ "\n")++ bits :: String -> Options -> IO Options+ bits string o =+ case parse_double string of+ Just n | n >= 0 -> return o { opt_digits = n * logBase 10 2 }+ Just n -> optfail ("Number of bits must not be negative -- " ++ string ++ "\n")+ _ -> optfail ("Invalid bits -- " ++ string ++ "\n")++ epsilon :: String -> Options -> IO Options+ epsilon string o =+ case parse_double string of+ Just eps | eps < 1 && eps > 0 -> return o { opt_digits = -logBase 10 eps }+ Just n -> optfail ("Epsilon must be between 0 and 1 -- " ++ string ++ "\n")+ _ -> optfail ("Invalid epsilon -- " ++ string ++ "\n")++ hex :: Options -> IO Options+ hex o = return o { opt_hex = True }++ stats :: Options -> IO Options+ stats o = return o { opt_stats = True }++ latex :: Options -> IO Options+ latex o = return o { opt_latex = True }++ count :: String -> Options -> IO Options+ count string o =+ case parse_int string of+ Just n | n >= 1 -> return o { opt_count = n }+ Just n -> optfail ("Invalid count, must be positive -- " ++ string ++ "\n")+ _ -> optfail ("Invalid count -- " ++ string ++ "\n")++ rseed :: String -> Options -> IO Options+ rseed string o =+ case reads string of+ [(g, "")] -> return o { opt_rseed = Just g }+ _ -> optfail ("Invalid random seed -- " ++ string ++ "\n")++-- | Process /argv/-style command line options into an 'Options' structure.+dopts :: [String] -> IO Options+dopts argv = do+ let (o, args, errs) = getOpt Permute options argv+ opts <- foldM (flip id) defaultOptions o+ when (errs /= []) $ do+ optfail (concat errs)+ case args of+ [] -> optfail "Missing argument: theta.\n"+ [string] -> do+ case parse_SymReal string of+ Just theta -> return opts { opt_theta = theta }+ _ -> optfail ("Invalid theta -- " ++ string ++ "\n")+ h1:h2:[] -> optfail ("Too many non-option arguments -- " ++ h1 ++ ", " ++ h2 ++ "\n")+ h1:h2:_ -> optfail ("Too many non-option arguments -- " ++ h1 ++ ", " ++ h2 ++ "...\n")++-- | Print usage message to 'stdout'.+usage :: IO ()+usage = do+ putStr (usageInfo header options) + where header = + "Usage: newsynth [OPTION...] <theta>\n"+ ++ "Arguments:\n"+ ++ " <theta> z-rotation angle. May be symbolic, e.g. pi/128\n"+ ++ "Options:"++-- ----------------------------------------------------------------------+-- * The main function++-- | Main function: read options, then execute the appropriate tasks.+main :: IO()+main = do+ -- Read options.+ argv <- getArgs+ options <- dopts argv+ let digits = opt_digits options+ let prec = digits * logBase 2 10+ let theta = opt_theta options+ let count = opt_count options+ let exponent = ceiling digits+ + -- Set random seed.+ g <- case opt_rseed options of+ Nothing -> newStdGen+ Just g -> return g+ + -- Expand random seed opt_count times.+ let gs = expand_seed count g++ -- Do it for each element of gs.+ stats <- sequence $ flip map (zip gs [1..]) $ \(g,n) -> do+ + when (count > 1 && (opt_stats options || opt_latex options)) $ do+ putStrLn ("Solution " ++ show n ++ ":")+ + -- Payload.+ t0 <- getCurrentTime+ let (m,err,ct) = newsynth_stats prec theta g+ gates = to_gates m+ if opt_hex options then+ printf "%x\n" (convert gates :: Integer)+ else+ putStrLn (if gates == [] then "I" else convert gates)+ t1 <- getCurrentTime++ -- Print optional statistics+ let tcount = length $ filter (==T) gates+ let secs = diffUTCTime t1 t0+ + when (opt_stats options || opt_latex options) $ do+ putStrLn ("Random seed: " ++ show g)+ putStrLn ("T-count: " ++ show tcount)+ + when (opt_stats options) $ do+ putStrLn ("Theta: " ++ show theta)+ putStrLn ("Epsilon: " ++ show_exp 10 exponent (Just digits))+ putStrLn ("Matrix: " ++ show m)+ putStrLn ("Actual error: " ++ show_exp 10 exponent err)+ putStrLn ("Runtime: " ++ show secs)+ putStrLn ("Candidates tried: " ++ show ct)+ putStrLn ("Time/candidate: " ++ show (secs / fromInteger ct))++ -- Optional LaTeX output+ when (opt_latex options) $ do+ putStrLn ("LaTeX Gates: " ++ showlatex gates)+ putStrLn ("LaTeX Theta: " ++ showlatex theta)+ putStrLn ("LaTeX Epsilon: " ++ showlatex_exp 5 exponent (Just digits))+ putStrLn ("LaTeX Matrix: " ++ showlatex (convert gates :: U2 DOmega))+ putStrLn ("LaTeX Actual error: " ++ showlatex_exp 5 exponent err)+ putStrLn ("LaTeX Runtime: " ++ show (round_to 2 secs))+ putStrLn ("LaTeX Candidates tried: " ++ show ct)+ putStrLn ("LaTeX Time/candidate: " ++ show (round_to 4 (secs / fromInteger ct)))+ + when (count > 1 && (opt_stats options || opt_latex options)) $ do+ putStrLn ""++ return (ct, secs)++ -- If count > 1, show summary stats.+ when (count > 1) $ do+ let (cts, secss) = unzip stats+ let ct_total = sum cts+ let secs_total = sum secss+ + when (opt_stats options || opt_latex options) $ do+ putStrLn "Summary:"+ putStrLn ("Number of runs: " ++ show count)+ putStrLn ("Total runtime: " ++ show secs_total)+ + when (opt_stats options) $ do+ putStrLn ("Average runtime: " ++ show (secs_total / fromInteger count))+ putStrLn ("Average candidates tried: " ++ show (fromInteger ct_total / fromInteger count :: Double))+ putStrLn ("Average time/candidate: " ++ show (secs_total / fromInteger ct_total))++ when (opt_latex options) $ do+ putStrLn ("LaTeX Average runtime: " ++ show (round_to 2 (secs_total / fromInteger count)))+ putStrLn ("LaTeX Average candidates tried: " ++ show (fromInteger ct_total / fromInteger count :: Double))+ putStrLn ("LaTeX Average time/candidate: " ++ show (round_to 4 (secs_total / fromInteger ct_total)))++-- ----------------------------------------------------------------------+-- * Miscellaneous++-- | Round a 'RealFrac' value to the given number of decimals. +round_to :: (RealFrac r) => Integer -> r -> r +round_to n x = fromInteger (round (x * 10^n)) / 10^n++-- | Show the number 10[sup -/x/] in the format 10^(-n) or+-- 1.23*10^(-n), with precision /d/ and exponent -/n/. A value of+-- 'Nothing' is treated as 0.+-- +-- For example, display @0.316*10^(-13)@ instead of @10^(-13.5)@.+show_exp :: (Show r, RealFrac r, Floating r, PrintfArg r) => Integer -> Integer -> Maybe r -> String+show_exp d n x+ | y == 1 = "10^(" ++ show (-n) ++ ")"+ | otherwise = printf ("%." ++ show d ++ "f") y ++ "*10^(" ++ show (-n) ++ ")"+ where+ y = case x of+ Nothing -> 0+ Just x -> round_to d (10 ** (fromInteger n - x))+ +-- | Show the number 10[sup -/x/] in the format @10^{-n}@ or+-- @1.23\\cdot 10^{-n}@, with precision /d/ and exponent -/n/. A value+-- of 'Nothing' is treated as 0.+showlatex_exp :: (Show r, RealFrac r, Floating r, PrintfArg r) => Integer -> Integer -> Maybe r -> String+showlatex_exp d n x + | y == 1 = "10^{" ++ show (-n) ++ "}"+ | otherwise = printf ("%." ++ show d ++ "f") y ++ "\\cdot 10^{" ++ show (-n) ++ "}"+ where+ y = case x of+ Nothing -> 0+ Just x -> round_to d (10 ** (fromInteger n - x))++-- | Expand a random seed /g/ into a list [/g/[sub 1], …, +-- /g/[sub /n/]] of /n/ random seeds. This is done in such a way that+-- /g/[sub 1] = /g/.+expand_seed :: (RandomGen g) => Integer -> g -> [g]+expand_seed 0 g = []+expand_seed n g = g:expand_seed (n-1) g' where+ (g', _) = split g