© Copyright IBM 2017, 2021.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
Qiskit Software | Version |
---|---|
qiskit-terra | 0.23.0.dev0+f52bb33 |
qiskit-aer | 0.11.1 |
qiskit-ignis | 0.7.1 |
qiskit-ibmq-provider | 0.19.2 |
qiskit-nature | 0.5.0 |
qiskit-optimization | 0.5.0 |
qiskit-machine-learning | 0.6.0 |
System information | |
Python version | 3.10.4 |
Python compiler | Clang 12.0.0 |
Python build | main, Mar 31 2022 03:38:35 |
OS | Darwin |
CPUs | 4 |
Memory (Gb) | 32.0 |
Wed Dec 07 11:02:26 2022 CET |
© Copyright IBM 2017, 2022.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
Qiskit Software | Version |
---|---|
qiskit-terra | 0.23.0 |
qiskit-aer | 0.11.1 |
qiskit-nature | 0.6.0 |
qiskit-finance | 0.4.0 |
qiskit-optimization | 0.5.0 |
qiskit-machine-learning | 0.6.0 |
System information | |
Python version | 3.9.13 |
Python compiler | Clang 13.0.1 |
Python build | main, May 27 2022 17:01:00 |
OS | Darwin |
CPUs | 2 |
Memory (Gb) | 12.0 |
Sun Jan 08 11:35:33 2023 EST |
© Copyright IBM 2017, 2023.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
Qiskit Software | Version |
---|---|
qiskit-terra | 0.23.3 |
qiskit-aer | 0.12.0 |
qiskit-ibmq-provider | 0.20.2 |
qiskit | 0.42.1 |
System information | |
Python version | 3.10.10 |
Python compiler | GCC 12.2.1 20230201 |
Python build | main, Mar 5 2023 22:26:53 |
OS | Linux |
CPUs | 32 |
Memory (Gb) | 125.66083908081055 |
Thu May 04 15:38:15 2023 EDT |
© Copyright IBM 2017, 2023.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
┌────────────┐┌─┐ \n", 541 | "q_0: ┤0 ├┤M├───\n", 542 | " │ Pauli(XX) │└╥┘┌─┐\n", 543 | "q_1: ┤1 ├─╫─┤M├\n", 544 | " └────────────┘ ║ └╥┘\n", 545 | "c: 2/═══════════════╩══╩═\n", 546 | " 0 1" 547 | ], 548 | "text/plain": [ 549 | " ┌────────────┐┌─┐ \n", 550 | "q_0: ┤0 ├┤M├───\n", 551 | " │ Pauli(XX) │└╥┘┌─┐\n", 552 | "q_1: ┤1 ├─╫─┤M├\n", 553 | " └────────────┘ ║ └╥┘\n", 554 | "c: 2/═══════════════╩══╩═\n", 555 | " 0 1 " 556 | ] 557 | }, 558 | "execution_count": 16, 559 | "metadata": {}, 560 | "output_type": "execute_result" 561 | } 562 | ], 563 | "source": [ 564 | "# Add to a circuit\n", 565 | "circ2 = QuantumCircuit(2, 2)\n", 566 | "circ2.append(Pauli('XX'), [0, 1])\n", 567 | "circ2.measure([0,1], [0,1])\n", 568 | "circ2.draw()" 569 | ] 570 | }, 571 | { 572 | "cell_type": "markdown", 573 | "metadata": {}, 574 | "source": [ 575 | "## Combining Operators\n", 576 | "\n", 577 | "Operators may be combined using several methods. \n", 578 | "\n", 579 | "### Tensor Product\n", 580 | "\n", 581 | "Two operators $A$ and $B$ may be combined into a tensor product operator $A\\otimes B$ using the `Operator.tensor` function. Note that if both $A$ and $B$ are single-qubit operators, then `A.tensor(B)` = $A\\otimes B$ will have the subsystems indexed as matrix $B$ on subsystem 0, and matrix $A$ on subsystem 1." 582 | ] 583 | }, 584 | { 585 | "cell_type": "code", 586 | "execution_count": 17, 587 | "metadata": { 588 | "ExecuteTime": { 589 | "end_time": "2019-08-21T09:04:14.208734Z", 590 | "start_time": "2019-08-21T09:04:14.201058Z" 591 | } 592 | }, 593 | "outputs": [ 594 | { 595 | "data": { 596 | "text/plain": [ 597 | "Operator([[ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", 598 | " [ 0.+0.j, -0.+0.j, 0.+0.j, -1.+0.j],\n", 599 | " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", 600 | " [ 0.+0.j, -1.+0.j, 0.+0.j, -0.+0.j]],\n", 601 | " input_dims=(2, 2), output_dims=(2, 2))" 602 | ] 603 | }, 604 | "execution_count": 17, 605 | "metadata": {}, 606 | "output_type": "execute_result" 607 | } 608 | ], 609 | "source": [ 610 | "A = Operator(Pauli('X'))\n", 611 | "B = Operator(Pauli('Z'))\n", 612 | "A.tensor(B)" 613 | ] 614 | }, 615 | { 616 | "cell_type": "markdown", 617 | "metadata": {}, 618 | "source": [ 619 | "### Tensor Expansion\n", 620 | "\n", 621 | "A closely related operation is `Operator.expand`, which acts like a tensor product but in the reverse order. Hence, for two operators $A$ and $B$ we have `A.expand(B)` = $B\\otimes A$ where the subsystems indexed as matrix $A$ on subsystem 0, and matrix $B$ on subsystem 1." 622 | ] 623 | }, 624 | { 625 | "cell_type": "code", 626 | "execution_count": 18, 627 | "metadata": { 628 | "ExecuteTime": { 629 | "end_time": "2019-08-21T09:04:14.899024Z", 630 | "start_time": "2019-08-21T09:04:14.891072Z" 631 | } 632 | }, 633 | "outputs": [ 634 | { 635 | "data": { 636 | "text/plain": [ 637 | "Operator([[ 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", 638 | " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", 639 | " [ 0.+0.j, 0.+0.j, -0.+0.j, -1.+0.j],\n", 640 | " [ 0.+0.j, 0.+0.j, -1.+0.j, -0.+0.j]],\n", 641 | " input_dims=(2, 2), output_dims=(2, 2))" 642 | ] 643 | }, 644 | "execution_count": 18, 645 | "metadata": {}, 646 | "output_type": "execute_result" 647 | } 648 | ], 649 | "source": [ 650 | "A = Operator(Pauli('X'))\n", 651 | "B = Operator(Pauli('Z'))\n", 652 | "A.expand(B)" 653 | ] 654 | }, 655 | { 656 | "cell_type": "markdown", 657 | "metadata": {}, 658 | "source": [ 659 | "### Composition\n", 660 | "\n", 661 | "We can also compose two operators $A$ and $B$ to implement matrix multiplication using the `Operator.compose` method. We have that `A.compose(B)` returns the operator with matrix $B.A$:" 662 | ] 663 | }, 664 | { 665 | "cell_type": "code", 666 | "execution_count": 19, 667 | "metadata": { 668 | "ExecuteTime": { 669 | "end_time": "2019-08-21T09:04:15.655155Z", 670 | "start_time": "2019-08-21T09:04:15.648295Z" 671 | } 672 | }, 673 | "outputs": [ 674 | { 675 | "data": { 676 | "text/plain": [ 677 | "Operator([[ 0.+0.j, 1.+0.j],\n", 678 | " [-1.+0.j, 0.+0.j]],\n", 679 | " input_dims=(2,), output_dims=(2,))" 680 | ] 681 | }, 682 | "execution_count": 19, 683 | "metadata": {}, 684 | "output_type": "execute_result" 685 | } 686 | ], 687 | "source": [ 688 | "A = Operator(Pauli('X'))\n", 689 | "B = Operator(Pauli('Z'))\n", 690 | "A.compose(B)" 691 | ] 692 | }, 693 | { 694 | "cell_type": "markdown", 695 | "metadata": {}, 696 | "source": [ 697 | "We can also compose in the reverse order by applying $B$ in front of $A$ using the `front` kwarg of `compose`: `A.compose(B, front=True)` = $A.B$:" 698 | ] 699 | }, 700 | { 701 | "cell_type": "code", 702 | "execution_count": 20, 703 | "metadata": { 704 | "ExecuteTime": { 705 | "end_time": "2019-08-21T09:04:16.460560Z", 706 | "start_time": "2019-08-21T09:04:16.452319Z" 707 | } 708 | }, 709 | "outputs": [ 710 | { 711 | "data": { 712 | "text/plain": [ 713 | "Operator([[ 0.+0.j, -1.+0.j],\n", 714 | " [ 1.+0.j, 0.+0.j]],\n", 715 | " input_dims=(2,), output_dims=(2,))" 716 | ] 717 | }, 718 | "execution_count": 20, 719 | "metadata": {}, 720 | "output_type": "execute_result" 721 | } 722 | ], 723 | "source": [ 724 | "A = Operator(Pauli('X'))\n", 725 | "B = Operator(Pauli('Z'))\n", 726 | "A.compose(B, front=True)" 727 | ] 728 | }, 729 | { 730 | "cell_type": "markdown", 731 | "metadata": {}, 732 | "source": [ 733 | "### Subsystem Composition\n", 734 | "\n", 735 | "Note that the previous compose requires that the total output dimension of the first operator $A$ is equal to total input dimension of the composed operator $B$ (and similarly, the output dimension of $B$ must be equal to the input dimension of $A$ when composing with `front=True`).\n", 736 | "\n", 737 | "We can also compose a smaller operator with a selection of subsystems on a larger operator using the `qargs` kwarg of `compose`, either with or without `front=True`. In this case, the relevant input and output dimensions of the subsystems being composed must match. *Note that the smaller operator must always be the argument of* `compose` *method.*\n", 738 | "\n", 739 | "For example, to compose a two-qubit gate with a three-qubit Operator:" 740 | ] 741 | }, 742 | { 743 | "cell_type": "code", 744 | "execution_count": 21, 745 | "metadata": { 746 | "ExecuteTime": { 747 | "end_time": "2019-08-21T09:04:17.113510Z", 748 | "start_time": "2019-08-21T09:04:17.105398Z" 749 | } 750 | }, 751 | "outputs": [ 752 | { 753 | "data": { 754 | "text/plain": [ 755 | "Operator([[ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j,\n", 756 | " 0.+0.j],\n", 757 | " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, -1.+0.j, 0.+0.j,\n", 758 | " 0.+0.j],\n", 759 | " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j,\n", 760 | " 0.+0.j],\n", 761 | " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", 762 | " -1.+0.j],\n", 763 | " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", 764 | " 0.+0.j],\n", 765 | " [ 0.+0.j, -1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", 766 | " 0.+0.j],\n", 767 | " [ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", 768 | " 0.+0.j],\n", 769 | " [ 0.+0.j, 0.+0.j, 0.+0.j, -1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", 770 | " 0.+0.j]],\n", 771 | " input_dims=(2, 2, 2), output_dims=(2, 2, 2))" 772 | ] 773 | }, 774 | "execution_count": 21, 775 | "metadata": {}, 776 | "output_type": "execute_result" 777 | } 778 | ], 779 | "source": [ 780 | "# Compose XZ with a 3-qubit identity operator\n", 781 | "op = Operator(np.eye(2 ** 3))\n", 782 | "XZ = Operator(Pauli('XZ'))\n", 783 | "op.compose(XZ, qargs=[0, 2])" 784 | ] 785 | }, 786 | { 787 | "cell_type": "code", 788 | "execution_count": 22, 789 | "metadata": { 790 | "ExecuteTime": { 791 | "end_time": "2019-08-21T09:04:17.324353Z", 792 | "start_time": "2019-08-21T09:04:17.315952Z" 793 | } 794 | }, 795 | "outputs": [ 796 | { 797 | "data": { 798 | "text/plain": [ 799 | "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j],\n", 800 | " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", 801 | " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],\n", 802 | " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j],\n", 803 | " [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", 804 | " [0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", 805 | " [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", 806 | " [0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n", 807 | " input_dims=(2, 2, 2), output_dims=(2, 2, 2))" 808 | ] 809 | }, 810 | "execution_count": 22, 811 | "metadata": {}, 812 | "output_type": "execute_result" 813 | } 814 | ], 815 | "source": [ 816 | "# Compose YX in front of the previous operator\n", 817 | "op = Operator(np.eye(2 ** 3))\n", 818 | "YX = Operator(Pauli('YX'))\n", 819 | "op.compose(YX, qargs=[0, 2], front=True)" 820 | ] 821 | }, 822 | { 823 | "cell_type": "markdown", 824 | "metadata": {}, 825 | "source": [ 826 | "### Linear combinations\n", 827 | "\n", 828 | "Operators may also be combined using standard linear operators for addition, subtraction and scalar multiplication by complex numbers. " 829 | ] 830 | }, 831 | { 832 | "cell_type": "code", 833 | "execution_count": 23, 834 | "metadata": { 835 | "ExecuteTime": { 836 | "end_time": "2019-08-21T09:04:18.829988Z", 837 | "start_time": "2019-08-21T09:04:18.812834Z" 838 | } 839 | }, 840 | "outputs": [ 841 | { 842 | "data": { 843 | "text/plain": [ 844 | "Operator([[-1.5+0.j, 0. +0.j, 0. +0.j, 0. +0.j],\n", 845 | " [ 0. +0.j, 1.5+0.j, 1. +0.j, 0. +0.j],\n", 846 | " [ 0. +0.j, 1. +0.j, 1.5+0.j, 0. +0.j],\n", 847 | " [ 0. +0.j, 0. +0.j, 0. +0.j, -1.5+0.j]],\n", 848 | " input_dims=(2, 2), output_dims=(2, 2))" 849 | ] 850 | }, 851 | "execution_count": 23, 852 | "metadata": {}, 853 | "output_type": "execute_result" 854 | } 855 | ], 856 | "source": [ 857 | "XX = Operator(Pauli('XX'))\n", 858 | "YY = Operator(Pauli('YY'))\n", 859 | "ZZ = Operator(Pauli('ZZ'))\n", 860 | "\n", 861 | "op = 0.5 * (XX + YY - 3 * ZZ)\n", 862 | "op" 863 | ] 864 | }, 865 | { 866 | "cell_type": "markdown", 867 | "metadata": {}, 868 | "source": [ 869 | "An important point is that while `tensor`, `expand` and `compose` will preserve the unitarity of unitary operators, linear combinations will not; hence, adding two unitary operators will, in general, result in a non-unitary operator:" 870 | ] 871 | }, 872 | { 873 | "cell_type": "code", 874 | "execution_count": 24, 875 | "metadata": { 876 | "ExecuteTime": { 877 | "end_time": "2019-08-21T09:04:19.151814Z", 878 | "start_time": "2019-08-21T09:04:19.147497Z" 879 | } 880 | }, 881 | "outputs": [ 882 | { 883 | "data": { 884 | "text/plain": [ 885 | "False" 886 | ] 887 | }, 888 | "execution_count": 24, 889 | "metadata": {}, 890 | "output_type": "execute_result" 891 | } 892 | ], 893 | "source": [ 894 | "op.is_unitary()" 895 | ] 896 | }, 897 | { 898 | "cell_type": "markdown", 899 | "metadata": {}, 900 | "source": [ 901 | "### Implicit Conversion to Operators\n", 902 | "\n", 903 | "Note that for all the following methods, if the second object is not already an `Operator` object, it will be implicitly converted into one by the method. This means that matrices can be passed in directly without being explicitly converted to an `Operator` first. If the conversion is not possible, an exception will be raised." 904 | ] 905 | }, 906 | { 907 | "cell_type": "code", 908 | "execution_count": 25, 909 | "metadata": { 910 | "ExecuteTime": { 911 | "end_time": "2019-08-21T09:04:20.045005Z", 912 | "start_time": "2019-08-21T09:04:20.039841Z" 913 | } 914 | }, 915 | "outputs": [ 916 | { 917 | "data": { 918 | "text/plain": [ 919 | "Operator([[0.+0.j, 1.+0.j],\n", 920 | " [1.+0.j, 0.+0.j]],\n", 921 | " input_dims=(2,), output_dims=(2,))" 922 | ] 923 | }, 924 | "execution_count": 25, 925 | "metadata": {}, 926 | "output_type": "execute_result" 927 | } 928 | ], 929 | "source": [ 930 | "# Compose with a matrix passed as a list\n", 931 | "Operator(np.eye(2)).compose([[0, 1], [1, 0]])" 932 | ] 933 | }, 934 | { 935 | "cell_type": "markdown", 936 | "metadata": {}, 937 | "source": [ 938 | "## Comparison of Operators\n", 939 | "\n", 940 | "Operators implement an equality method that can be used to check if two operators are approximately equal. " 941 | ] 942 | }, 943 | { 944 | "cell_type": "code", 945 | "execution_count": 26, 946 | "metadata": { 947 | "ExecuteTime": { 948 | "end_time": "2019-08-21T09:04:20.821642Z", 949 | "start_time": "2019-08-21T09:04:20.815611Z" 950 | } 951 | }, 952 | "outputs": [ 953 | { 954 | "data": { 955 | "text/plain": [ 956 | "True" 957 | ] 958 | }, 959 | "execution_count": 26, 960 | "metadata": {}, 961 | "output_type": "execute_result" 962 | } 963 | ], 964 | "source": [ 965 | "Operator(Pauli('X')) == Operator(XGate())" 966 | ] 967 | }, 968 | { 969 | "cell_type": "markdown", 970 | "metadata": {}, 971 | "source": [ 972 | "Note that this checks that each matrix element of the operators is approximately equal; two unitaries that differ by a global phase will not be considered equal:" 973 | ] 974 | }, 975 | { 976 | "cell_type": "code", 977 | "execution_count": 27, 978 | "metadata": { 979 | "ExecuteTime": { 980 | "end_time": "2019-08-21T09:04:21.146256Z", 981 | "start_time": "2019-08-21T09:04:21.141242Z" 982 | } 983 | }, 984 | "outputs": [ 985 | { 986 | "data": { 987 | "text/plain": [ 988 | "False" 989 | ] 990 | }, 991 | "execution_count": 27, 992 | "metadata": {}, 993 | "output_type": "execute_result" 994 | } 995 | ], 996 | "source": [ 997 | "Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())" 998 | ] 999 | }, 1000 | { 1001 | "cell_type": "markdown", 1002 | "metadata": {}, 1003 | "source": [ 1004 | "### Process Fidelity\n", 1005 | "\n", 1006 | "We may also compare operators using the `process_fidelity` function from the *Quantum Information* module. This is an information theoretic quantity for how close two quantum channels are to each other, and in the case of unitary operators it does not depend on global phase." 1007 | ] 1008 | }, 1009 | { 1010 | "cell_type": "code", 1011 | "execution_count": 28, 1012 | "metadata": { 1013 | "ExecuteTime": { 1014 | "end_time": "2019-08-21T09:04:22.171481Z", 1015 | "start_time": "2019-08-21T09:04:22.147477Z" 1016 | } 1017 | }, 1018 | "outputs": [ 1019 | { 1020 | "name": "stdout", 1021 | "output_type": "stream", 1022 | "text": [ 1023 | "Process fidelity = 1.0\n" 1024 | ] 1025 | } 1026 | ], 1027 | "source": [ 1028 | "# Two operators which differ only by phase\n", 1029 | "op_a = Operator(XGate()) \n", 1030 | "op_b = np.exp(1j * 0.5) * Operator(XGate())\n", 1031 | "\n", 1032 | "# Compute process fidelity\n", 1033 | "F = process_fidelity(op_a, op_b)\n", 1034 | "print('Process fidelity =', F)" 1035 | ] 1036 | }, 1037 | { 1038 | "cell_type": "markdown", 1039 | "metadata": {}, 1040 | "source": [ 1041 | "Note that process fidelity is generally only a valid measure of closeness if the input operators are unitary (or CP in the case of quantum channels), and an exception will be raised if the inputs are not CP." 1042 | ] 1043 | }, 1044 | { 1045 | "cell_type": "code", 1046 | "execution_count": 29, 1047 | "metadata": { 1048 | "ExecuteTime": { 1049 | "end_time": "2019-08-21T09:04:44.743744Z", 1050 | "start_time": "2019-08-21T09:04:44.734826Z" 1051 | } 1052 | }, 1053 | "outputs": [ 1054 | { 1055 | "data": { 1056 | "text/html": [ 1057 | "
Qiskit Software | Version |
---|---|
qiskit-terra | 0.20.2 |
qiskit-aer | 0.10.4 |
qiskit-ignis | 0.7.1 |
qiskit-ibmq-provider | 0.19.1 |
qiskit | 0.36.2 |
System information | |
Python version | 3.9.9 |
Python compiler | GCC 11.1.0 |
Python build | main, Dec 29 2021 22:19:36 |
OS | Linux |
CPUs | 32 |
Memory (Gb) | 125.64821243286133 |
Wed Jun 22 15:52:11 2022 EDT |
© Copyright IBM 2017, 2022.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.