{"action":"created","comment":{"url":"https://api.github.com/repos/XanaduAI/pennylane/pulls/comments/342866871","pull_request_review_id":312122141,"id":342866871,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDM0Mjg2Njg3MQ==","diff_hunk":"@@ -0,0 +1,528 @@\n+# Copyright 2018 Xanadu Quantum Technologies Inc.\n+\n+# Licensed under the Apache License, Version 2.0 (the \"License\");\n+# you may not use this file except in compliance with the License.\n+# You may obtain a copy of the License at\n+\n+#     http://www.apache.org/licenses/LICENSE-2.0\n+\n+# Unless required by applicable law or agreed to in writing, software\n+# distributed under the License is distributed on an \"AS IS\" BASIS,\n+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+# See the License for the specific language governing permissions and\n+# limitations under the License.\n+\"\"\"\n+Differentiable quantum nodes.\n+\"\"\"\n+\n+from collections.abc import Sequence\n+import copy\n+\n+import autograd.extend\n+import autograd.builtins\n+import numpy as np\n+\n+import pennylane as qml\n+from pennylane.operation import ObservableReturnTypes, CV\n+from pennylane.utils import _flatten, unflatten, _inv_dict\n+from pennylane.qnode_new.qnode import QNode, QuantumFunctionError\n+\n+\n+\n+class JacobianQNode(QNode):\n+    \"\"\"Quantum node that can be differentiated with respect to its positional parameters.\n+    \"\"\"\n+    interface = None  #: str, None: automatic differentiation interface used by the node, if any\n+\n+    def __init__(self, func, device, mutable=True, properties=None):\n+        super().__init__(func, device, mutable=mutable, properties=properties)\n+\n+        #: dict[int, str]: map from free parameter index to the gradient method to be used with that parameter\n+        self.par_to_grad_method = None\n+\n+    def __repr__(self):\n+        \"\"\"String representation.\"\"\"\n+        detail = \"<JNode: device='{}', func={}, wires={}, interface={}>\"\n+        return detail.format(self.device.short_name, self.func.__name__, self.num_wires, self.interface)\n+\n+    def _construct(self, args, kwargs):\n+        \"\"\"Constructs the quantum circuit graph by calling the quantum function.\n+\n+        Like :meth:`.QNode._construct`, additionally determines the best gradient computation method\n+        for each free parameter.\n+        \"\"\"\n+        super()._construct(args, kwargs)\n+        self.par_to_grad_method = {k: self._best_method(k) for k in self.variable_deps}\n+\n+        temp = [str(ob) for ob in self.circuit.observables if ob.return_type is ObservableReturnTypes.Sample]\n+        if temp:\n+            raise QuantumFunctionError(\"Circuits that include sampling can not be differentiated. \"\n+                                       \"The following observables include sampling: {}\".format('; '.join(temp)))\n+\n+    def _best_method(self, idx):\n+        \"\"\"Determine the correct partial derivative computation method for a free parameter.\n+\n+        Use the analytic method iff every gate that depends on the parameter supports it.\n+        If not, use the finite difference method only (one would have to use it anyway).\n+\n+        Note that if even one dependent Operation does not support differentiation,\n+        we cannot differentiate with respect to this parameter at all.\n+\n+        Args:\n+            idx (int): free parameter index\n+        Returns:\n+            str: partial derivative method to be used\n+        \"\"\"\n+        # TODO make sure Identity is recognized as gaussian\n+\n+        # pylint: disable=too-many-return-statements\n+        def best_for_op(op):\n+            \"Returns the best gradient method for the Operation op.\"\n+            # for discrete operations, other ops do not affect the choice\n+\n+            if not isinstance(op, CV):\n+                return op.grad_method\n+\n+            # for CV ops it is more complicated\n+            if op.grad_method == \"A\":\n+                # op is Gaussian and has the heisenberg_* methods\n+\n+                obs_successors = self._op_descendants(op, 'E')\n+                if not obs_successors:\n+                    # op is not succeeded by any observables, thus analytic method is OK    FIXME actually we can ignore it....\n+                    return 'A'\n+\n+                # check that all successor ops are also Gaussian\n+                successor_ops = self._op_descendants(op, 'G')\n+                if not all(x.supports_heisenberg for x in successor_ops):\n+                    non_gaussian_ops = [x for x in successor_ops if not x.supports_heisenberg]\n+                    # a non-Gaussian successor is OK if it isn't succeeded by any observables\n+                    for x in non_gaussian_ops:\n+                        if self._op_descendants(x, 'E'):\n+                            return 'F'\n+\n+                # check successor EVs, if any order-2 observables are found return 'A2', else return 'A'\n+                for observable in obs_successors:\n+                    if observable.ev_order is None:\n+                        # ev_order of None corresponds to a non-Gaussian observable\n+                        return 'F'\n+                    if observable.ev_order == 2:\n+                        if observable.return_type is ObservableReturnTypes.Variance:\n+                            # second order observables don't support\n+                            # analytic diff of variances\n+                            return 'F'\n+                        op.grad_method = 'A2'  # bit of a hack\n+                return 'A'\n+            return op.grad_method\n+\n+        # operations that depend on this free parameter\n+        ops = [d.op for d in self.variable_deps[idx]]\n+        methods = list(map(best_for_op, ops))\n+\n+        if all(k in ('A', 'A2') for k in methods):\n+            return 'A'\n+\n+        if None in methods:\n+            return None\n+\n+        return 'F'\n+\n+    def jacobian(self, args, kwargs=None, *, wrt=None, method='B', options=None):\n+        r\"\"\"Compute the Jacobian of the QNode.\n+\n+        Returns the Jacobian of the parametrized quantum circuit encapsulated in the QNode.\n+        The Jacobian is returned as a two-dimensional array. The (possibly nested) input arguments\n+        of the QNode are :func:`flattened <_flatten>` so the QNode can be interpreted as a simple\n+        :math:`\\mathbb{R}^m \\to \\mathbb{R}^n` function.\n+\n+        The Jacobian can be computed using several methods:\n+\n+        * Finite differences (``'F'``). The first order method evaluates the circuit at\n+          :math:`n+1` points of the parameter space, the second order method at :math:`2n` points,\n+          where ``n = len(wrt)``.\n+\n+        * Analytic method (``'A'``). Works for all one-parameter gates where the generator\n+          only has two unique eigenvalues; this includes one-parameter single-qubit gates.\n+          Additionally, can be used in CV systems for Gaussian circuits containing only first-\n+          and second-order observables.\n+          The circuit is evaluated twice for each incidence of each parameter in the circuit.\n+\n+        * Best known method for each parameter (``'B'``): uses the analytic method if\n+          possible, otherwise finite difference.\n+\n+        * Device method (``'D'``): Delegates the computation of the Jacobian to the\n+          device executing the circuit.\n+\n+        .. note::\n+           The finite difference method is sensitive to statistical noise in the circuit output,\n+           since it compares the output at two points infinitesimally close to each other. Hence the\n+           'F' method requires exact expectation values, i.e., ``analytic=True`` in simulation plugins.\n+\n+        Args:\n+            args (nested Iterable[float] or float): positional arguments to the quantum function (differentiable)\n+            kwargs (dict[str, Any]): auxiliary arguments to the quantum function (not differentiable)\n+            wrt (Sequence[int] or None): Indices of the flattened positional parameters with respect\n+                to which to compute the Jacobian. None means all the parameters.\n+                Note that you cannot compute the Jacobian with respect to the kwargs.\n+            method (str): Jacobian computation method, in ``{'F', 'A', 'B', 'D'}``, see above\n+            options (dict[str, Any]): additional options\n+\n+                * h (float): finite difference method step size\n+                * order (int): finite difference method order, 1 or 2\n+                * force_order2 (bool): force the analytic method to use the second-order method\n+                * shots (int): number of times the circuit is evaluated (or sampled) to estimate expectation values FIXME is this used?\n+\n+        Returns:\n+            array[float]: Jacobian, shape ``(n, len(wrt))``, where ``n`` is the number of outputs returned by the QNode\n+        \"\"\"\n+        options = options or {}\n+\n+        # arrays are not Sequences... but they ARE Iterables? FIXME decide which types we accept! cf. _flatten\n+        if not isinstance(args, (Sequence, np.ndarray)):\n+            args = (args,)\n+        kwargs = kwargs or {}\n+\n+        # apply defaults\n+        kwargs = self._default_args(kwargs)\n+\n+        # (re-)construct the circuit if necessary\n+        if self.circuit is None or self.mutable:\n+            self._construct(args, kwargs)\n+\n+        # In the following, to evaluate the Jacobian we call self.evaluate several times using\n+        # modified args (and possibly modified circuit Operators).\n+        # We do not want evaluate to call _construct again. This would only be necessary if the\n+        # auxiliary args changed, since only they can change the structure of the circuit,\n+        # and we do not modify them. To achieve this, we temporarily make the circuit immutable.\n+        mutable = self.mutable\n+        self.mutable = False\n+\n+        if wrt is None:\n+            wrt = range(self.num_variables)\n+        else:\n+            if min(wrt) < 0 or max(wrt) >= self.num_variables:\n+                raise ValueError(\"Tried to compute the gradient wrt. free parameters {} \"\n+                                 \"(this node has {} free parameters).\".format(wrt, self.num_variables))\n+            if len(wrt) != len(set(wrt)):  # set removes duplicates\n+                raise ValueError(\"Parameter indices must be unique.\")\n+\n+        # check if the method can be used on the requested parameters\n+        method_map = _inv_dict(self.par_to_grad_method)\n+        def inds_using(m):\n+            \"\"\"Intersection of ``wrt`` with free params indices whose best grad method is m.\"\"\"\n+            return method_map.get(m, set()).intersection(wrt)\n+\n+        # are we trying to differentiate wrt. params that don't support any method?\n+        bad = inds_using(None)\n+        if bad:\n+            raise ValueError('Cannot differentiate wrt. parameters {}.'.format(bad))\n+\n+        if method in ('A', 'F'):\n+            if method == 'A':\n+                bad = inds_using('F')\n+                if bad:\n+                    raise ValueError(\"The analytic gradient method cannot be \"\n+                                     \"used with the parameters {}.\".format(bad))\n+            method = {k: method for k in wrt}\n+        elif method == 'B':\n+            # use best known method for each parameter\n+            method = self.par_to_grad_method\n+        else:\n+            raise ValueError('Unknown gradient method.')\n+\n+        if 'F' in method.values():\n+            if options.get('order', 1) == 1:\n+                # the value of the circuit at args, computed only once here\n+                options['y0'] = np.asarray(self.evaluate(args, kwargs))\n+\n+        # flatten the nested Sequence of input arguments\n+        flat_args = np.array(list(_flatten(args)), dtype=float)\n+        variances_required = any(ob.return_type is ObservableReturnTypes.Variance for ob in self.circuit.observables)\n+\n+        # compute the partial derivative wrt. each parameter using the appropriate method\n+        grad = np.zeros((self.output_dim, len(wrt)), dtype=float)\n+        for i, k in enumerate(wrt):\n+            if k not in self.variable_deps:\n+                # unused parameter, partial derivatives wrt. it are zero\n+                continue\n+\n+            par_method = method[k]\n+            if par_method == 'A':\n+                if variances_required:\n+                    grad[:, i] = self._pd_parameter_shift_var(k, flat_args, kwargs)\n+                else:\n+                    grad[:, i] = self._pd_parameter_shift(k, flat_args, kwargs, **options)\n+            elif par_method == 'F':\n+                grad[:, i] = self._pd_finite_diff(k, flat_args, kwargs, **options)\n+            else:\n+                raise ValueError('Unknown gradient method.')","path":"pennylane/qnode_new/jacobian.py","position":258,"original_position":258,"commit_id":"b7b39134b3939cb9af456cf4b8303b67eed42e0c","original_commit_id":"b7b39134b3939cb9af456cf4b8303b67eed42e0c","user":{"login":"co9olguy","id":7213358,"node_id":"MDQ6VXNlcjcyMTMzNTg=","avatar_url":"https://avatars2.githubusercontent.com/u/7213358?v=4","gravatar_id":"","url":"https://api.github.com/users/co9olguy","html_url":"https://github.com/co9olguy","followers_url":"https://api.github.com/users/co9olguy/followers","following_url":"https://api.github.com/users/co9olguy/following{/other_user}","gists_url":"https://api.github.com/users/co9olguy/gists{/gist_id}","starred_url":"https://api.github.com/users/co9olguy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/co9olguy/subscriptions","organizations_url":"https://api.github.com/users/co9olguy/orgs","repos_url":"https://api.github.com/users/co9olguy/repos","events_url":"https://api.github.com/users/co9olguy/events{/privacy}","received_events_url":"https://api.github.com/users/co9olguy/received_events","type":"User","site_admin":false},"body":"what about the new `D` method? It might be expected from the docstring that you can use `D`, but this error message would be contradictory","created_at":"2019-11-06T00:32:16Z","updated_at":"2019-11-06T00:47:34Z","html_url":"https://github.com/XanaduAI/pennylane/pull/398#discussion_r342866871","pull_request_url":"https://api.github.com/repos/XanaduAI/pennylane/pulls/398","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/XanaduAI/pennylane/pulls/comments/342866871"},"html":{"href":"https://github.com/XanaduAI/pennylane/pull/398#discussion_r342866871"},"pull_request":{"href":"https://api.github.com/repos/XanaduAI/pennylane/pulls/398"}}},"pull_request":{"url":"https://api.github.com/repos/XanaduAI/pennylane/pulls/398","id":336901262,"node_id":"MDExOlB1bGxSZXF1ZXN0MzM2OTAxMjYy","html_url":"https://github.com/XanaduAI/pennylane/pull/398","diff_url":"https://github.com/XanaduAI/pennylane/pull/398.diff","patch_url":"https://github.com/XanaduAI/pennylane/pull/398.patch","issue_url":"https://api.github.com/repos/XanaduAI/pennylane/issues/398","number":398,"state":"open","locked":false,"title":"QNode refactor, part 3.","user":{"login":"smite","id":1044345,"node_id":"MDQ6VXNlcjEwNDQzNDU=","avatar_url":"https://avatars2.githubusercontent.com/u/1044345?v=4","gravatar_id":"","url":"https://api.github.com/users/smite","html_url":"https://github.com/smite","followers_url":"https://api.github.com/users/smite/followers","following_url":"https://api.github.com/users/smite/following{/other_user}","gists_url":"https://api.github.com/users/smite/gists{/gist_id}","starred_url":"https://api.github.com/users/smite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/smite/subscriptions","organizations_url":"https://api.github.com/users/smite/orgs","repos_url":"https://api.github.com/users/smite/repos","events_url":"https://api.github.com/users/smite/events{/privacy}","received_events_url":"https://api.github.com/users/smite/received_events","type":"User","site_admin":false},"body":"**Context:**\r\n\r\nThe QNode class has gotten big, unwieldy and hard to understand. This series of PRs replaces it with a hierarchy of simpler classes, while keeping the old QNode class intact until the very end.\r\n\r\n**Description of the Change:**\r\n\r\n* Adds the `qnode_new.jacobian` module containing two new classes.\r\n* `JacobianQNode` inherits `QNode` and adds the functionality for computing the Jacobian of the node. It does not interact with any autodiff framework.\r\n* `AutogradQNode` inherits `JacobianQNode` and adds stuff that makes the node work with Autograd.\r\n* Adds `tests/qnode_new/test_qnode_jacobian.py` and `tests/qnode_new/test_qnode_autograd.py` containing tests for the new classes.\r\n\r\n**Benefits:**\r\n\r\n* Does not disrupt the existing QNode class, the rest of the PennyLane code should run as before.\r\n* We can most likely add support for TensorFlow and PyTorch compatible nodes along the lines of `AutogradQNode`. \r\n\r\n**Possible Drawbacks:**\r\n\r\nNot a drawback as such, but currently the test suite for the Jacobian functionality (which was mostly inherited from the old `QNode`) is quite unorganized, not every test has a good, explicit justification for its existence, and there are many overlapping tests. Maybe the tests can be reorganized and improved in future PRs.\r\n\r\n**Related GitHub Issues:**\r\n","created_at":"2019-11-05T17:39:10Z","updated_at":"2019-11-06T00:47:33Z","closed_at":null,"merged_at":null,"merge_commit_sha":"aef6b49e34430f70801e6a4a670f807bb8032f78","assignee":{"login":"smite","id":1044345,"node_id":"MDQ6VXNlcjEwNDQzNDU=","avatar_url":"https://avatars2.githubusercontent.com/u/1044345?v=4","gravatar_id":"","url":"https://api.github.com/users/smite","html_url":"https://github.com/smite","followers_url":"https://api.github.com/users/smite/followers","following_url":"https://api.github.com/users/smite/following{/other_user}","gists_url":"https://api.github.com/users/smite/gists{/gist_id}","starred_url":"https://api.github.com/users/smite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/smite/subscriptions","organizations_url":"https://api.github.com/users/smite/orgs","repos_url":"https://api.github.com/users/smite/repos","events_url":"https://api.github.com/users/smite/events{/privacy}","received_events_url":"https://api.github.com/users/smite/received_events","type":"User","site_admin":false},"assignees":[{"login":"smite","id":1044345,"node_id":"MDQ6VXNlcjEwNDQzNDU=","avatar_url":"https://avatars2.githubusercontent.com/u/1044345?v=4","gravatar_id":"","url":"https://api.github.com/users/smite","html_url":"https://github.com/smite","followers_url":"https://api.github.com/users/smite/followers","following_url":"https://api.github.com/users/smite/following{/other_user}","gists_url":"https://api.github.com/users/smite/gists{/gist_id}","starred_url":"https://api.github.com/users/smite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/smite/subscriptions","organizations_url":"https://api.github.com/users/smite/orgs","repos_url":"https://api.github.com/users/smite/repos","events_url":"https://api.github.com/users/smite/events{/privacy}","received_events_url":"https://api.github.com/users/smite/received_events","type":"User","site_admin":false}],"requested_reviewers":[{"login":"josh146","id":2959003,"node_id":"MDQ6VXNlcjI5NTkwMDM=","avatar_url":"https://avatars0.githubusercontent.com/u/2959003?v=4","gravatar_id":"","url":"https://api.github.com/users/josh146","html_url":"https://github.com/josh146","followers_url":"https://api.github.com/users/josh146/followers","following_url":"https://api.github.com/users/josh146/following{/other_user}","gists_url":"https://api.github.com/users/josh146/gists{/gist_id}","starred_url":"https://api.github.com/users/josh146/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josh146/subscriptions","organizations_url":"https://api.github.com/users/josh146/orgs","repos_url":"https://api.github.com/users/josh146/repos","events_url":"https://api.github.com/users/josh146/events{/privacy}","received_events_url":"https://api.github.com/users/josh146/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[{"id":1358179231,"node_id":"MDU6TGFiZWwxMzU4MTc5MjMx","url":"https://api.github.com/repos/XanaduAI/pennylane/labels/WIP","name":"WIP","color":"488aba","default":false},{"id":944708362,"node_id":"MDU6TGFiZWw5NDQ3MDgzNjI=","url":"https://api.github.com/repos/XanaduAI/pennylane/labels/core","name":"core","color":"3fc4e2","default":false},{"id":904999236,"node_id":"MDU6TGFiZWw5MDQ5OTkyMzY=","url":"https://api.github.com/repos/XanaduAI/pennylane/labels/enhancement","name":"enhancement","color":"a2eeef","default":true},{"id":1363932814,"node_id":"MDU6TGFiZWwxMzYzOTMyODE0","url":"https://api.github.com/repos/XanaduAI/pennylane/labels/tests","name":"tests","color":"c401a7","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/XanaduAI/pennylane/pulls/398/commits","review_comments_url":"https://api.github.com/repos/XanaduAI/pennylane/pulls/398/comments","review_comment_url":"https://api.github.com/repos/XanaduAI/pennylane/pulls/comments{/number}","comments_url":"https://api.github.com/repos/XanaduAI/pennylane/issues/398/comments","statuses_url":"https://api.github.com/repos/XanaduAI/pennylane/statuses/b7b39134b3939cb9af456cf4b8303b67eed42e0c","head":{"label":"XanaduAI:qnode_refactor3","ref":"qnode_refactor3","sha":"b7b39134b3939cb9af456cf4b8303b67eed42e0c","user":{"login":"XanaduAI","id":31855630,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxODU1NjMw","avatar_url":"https://avatars3.githubusercontent.com/u/31855630?v=4","gravatar_id":"","url":"https://api.github.com/users/XanaduAI","html_url":"https://github.com/XanaduAI","followers_url":"https://api.github.com/users/XanaduAI/followers","following_url":"https://api.github.com/users/XanaduAI/following{/other_user}","gists_url":"https://api.github.com/users/XanaduAI/gists{/gist_id}","starred_url":"https://api.github.com/users/XanaduAI/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/XanaduAI/subscriptions","organizations_url":"https://api.github.com/users/XanaduAI/orgs","repos_url":"https://api.github.com/users/XanaduAI/repos","events_url":"https://api.github.com/users/XanaduAI/events{/privacy}","received_events_url":"https://api.github.com/users/XanaduAI/received_events","type":"Organization","site_admin":false},"repo":{"id":129936360,"node_id":"MDEwOlJlcG9zaXRvcnkxMjk5MzYzNjA=","name":"pennylane","full_name":"XanaduAI/pennylane","private":false,"owner":{"login":"XanaduAI","id":31855630,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxODU1NjMw","avatar_url":"https://avatars3.githubusercontent.com/u/31855630?v=4","gravatar_id":"","url":"https://api.github.com/users/XanaduAI","html_url":"https://github.com/XanaduAI","followers_url":"https://api.github.com/users/XanaduAI/followers","following_url":"https://api.github.com/users/XanaduAI/following{/other_user}","gists_url":"https://api.github.com/users/XanaduAI/gists{/gist_id}","starred_url":"https://api.github.com/users/XanaduAI/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/XanaduAI/subscriptions","organizations_url":"https://api.github.com/users/XanaduAI/orgs","repos_url":"https://api.github.com/users/XanaduAI/repos","events_url":"https://api.github.com/users/XanaduAI/events{/privacy}","received_events_url":"https://api.github.com/users/XanaduAI/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/XanaduAI/pennylane","description":"PennyLane is a cross-platform Python library for quantum machine learning, automatic differentiation, and optimization of hybrid quantum-classical computations","fork":false,"url":"https://api.github.com/repos/XanaduAI/pennylane","forks_url":"https://api.github.com/repos/XanaduAI/pennylane/forks","keys_url":"https://api.github.com/repos/XanaduAI/pennylane/keys{/key_id}","collaborators_url":"https://api.github.com/repos/XanaduAI/pennylane/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/XanaduAI/pennylane/teams","hooks_url":"https://api.github.com/repos/XanaduAI/pennylane/hooks","issue_events_url":"https://api.github.com/repos/XanaduAI/pennylane/issues/events{/number}","events_url":"https://api.github.com/repos/XanaduAI/pennylane/events","assignees_url":"https://api.github.com/repos/XanaduAI/pennylane/assignees{/user}","branches_url":"https://api.github.com/repos/XanaduAI/pennylane/branches{/branch}","tags_url":"https://api.github.com/repos/XanaduAI/pennylane/tags","blobs_url":"https://api.github.com/repos/XanaduAI/pennylane/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/XanaduAI/pennylane/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/XanaduAI/pennylane/git/refs{/sha}","trees_url":"https://api.github.com/repos/XanaduAI/pennylane/git/trees{/sha}","statuses_url":"https://api.github.com/repos/XanaduAI/pennylane/statuses/{sha}","languages_url":"https://api.github.com/repos/XanaduAI/pennylane/languages","stargazers_url":"https://api.github.com/repos/XanaduAI/pennylane/stargazers","contributors_url":"https://api.github.com/repos/XanaduAI/pennylane/contributors","subscribers_url":"https://api.github.com/repos/XanaduAI/pennylane/subscribers","subscription_url":"https://api.github.com/repos/XanaduAI/pennylane/subscription","commits_url":"https://api.github.com/repos/XanaduAI/pennylane/commits{/sha}","git_commits_url":"https://api.github.com/repos/XanaduAI/pennylane/git/commits{/sha}","comments_url":"https://api.github.com/repos/XanaduAI/pennylane/comments{/number}","issue_comment_url":"https://api.github.com/repos/XanaduAI/pennylane/issues/comments{/number}","contents_url":"https://api.github.com/repos/XanaduAI/pennylane/contents/{+path}","compare_url":"https://api.github.com/repos/XanaduAI/pennylane/compare/{base}...{head}","merges_url":"https://api.github.com/repos/XanaduAI/pennylane/merges","archive_url":"https://api.github.com/repos/XanaduAI/pennylane/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/XanaduAI/pennylane/downloads","issues_url":"https://api.github.com/repos/XanaduAI/pennylane/issues{/number}","pulls_url":"https://api.github.com/repos/XanaduAI/pennylane/pulls{/number}","milestones_url":"https://api.github.com/repos/XanaduAI/pennylane/milestones{/number}","notifications_url":"https://api.github.com/repos/XanaduAI/pennylane/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/XanaduAI/pennylane/labels{/name}","releases_url":"https://api.github.com/repos/XanaduAI/pennylane/releases{/id}","deployments_url":"https://api.github.com/repos/XanaduAI/pennylane/deployments","created_at":"2018-04-17T16:45:42Z","updated_at":"2019-11-05T22:50:35Z","pushed_at":"2019-11-05T23:52:06Z","git_url":"git://github.com/XanaduAI/pennylane.git","ssh_url":"git@github.com:XanaduAI/pennylane.git","clone_url":"https://github.com/XanaduAI/pennylane.git","svn_url":"https://github.com/XanaduAI/pennylane","homepage":"https://pennylane.ai","size":23144,"stargazers_count":372,"watchers_count":372,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":92,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":27,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":92,"open_issues":27,"watchers":372,"default_branch":"master"}},"base":{"label":"XanaduAI:master","ref":"master","sha":"9b091a9306b9037df8308524df5f6f48acb6e24c","user":{"login":"XanaduAI","id":31855630,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxODU1NjMw","avatar_url":"https://avatars3.githubusercontent.com/u/31855630?v=4","gravatar_id":"","url":"https://api.github.com/users/XanaduAI","html_url":"https://github.com/XanaduAI","followers_url":"https://api.github.com/users/XanaduAI/followers","following_url":"https://api.github.com/users/XanaduAI/following{/other_user}","gists_url":"https://api.github.com/users/XanaduAI/gists{/gist_id}","starred_url":"https://api.github.com/users/XanaduAI/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/XanaduAI/subscriptions","organizations_url":"https://api.github.com/users/XanaduAI/orgs","repos_url":"https://api.github.com/users/XanaduAI/repos","events_url":"https://api.github.com/users/XanaduAI/events{/privacy}","received_events_url":"https://api.github.com/users/XanaduAI/received_events","type":"Organization","site_admin":false},"repo":{"id":129936360,"node_id":"MDEwOlJlcG9zaXRvcnkxMjk5MzYzNjA=","name":"pennylane","full_name":"XanaduAI/pennylane","private":false,"owner":{"login":"XanaduAI","id":31855630,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxODU1NjMw","avatar_url":"https://avatars3.githubusercontent.com/u/31855630?v=4","gravatar_id":"","url":"https://api.github.com/users/XanaduAI","html_url":"https://github.com/XanaduAI","followers_url":"https://api.github.com/users/XanaduAI/followers","following_url":"https://api.github.com/users/XanaduAI/following{/other_user}","gists_url":"https://api.github.com/users/XanaduAI/gists{/gist_id}","starred_url":"https://api.github.com/users/XanaduAI/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/XanaduAI/subscriptions","organizations_url":"https://api.github.com/users/XanaduAI/orgs","repos_url":"https://api.github.com/users/XanaduAI/repos","events_url":"https://api.github.com/users/XanaduAI/events{/privacy}","received_events_url":"https://api.github.com/users/XanaduAI/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/XanaduAI/pennylane","description":"PennyLane is a cross-platform Python library for quantum machine learning, automatic differentiation, and optimization of hybrid quantum-classical computations","fork":false,"url":"https://api.github.com/repos/XanaduAI/pennylane","forks_url":"https://api.github.com/repos/XanaduAI/pennylane/forks","keys_url":"https://api.github.com/repos/XanaduAI/pennylane/keys{/key_id}","collaborators_url":"https://api.github.com/repos/XanaduAI/pennylane/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/XanaduAI/pennylane/teams","hooks_url":"https://api.github.com/repos/XanaduAI/pennylane/hooks","issue_events_url":"https://api.github.com/repos/XanaduAI/pennylane/issues/events{/number}","events_url":"https://api.github.com/repos/XanaduAI/pennylane/events","assignees_url":"https://api.github.com/repos/XanaduAI/pennylane/assignees{/user}","branches_url":"https://api.github.com/repos/XanaduAI/pennylane/branches{/branch}","tags_url":"https://api.github.com/repos/XanaduAI/pennylane/tags","blobs_url":"https://api.github.com/repos/XanaduAI/pennylane/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/XanaduAI/pennylane/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/XanaduAI/pennylane/git/refs{/sha}","trees_url":"https://api.github.com/repos/XanaduAI/pennylane/git/trees{/sha}","statuses_url":"https://api.github.com/repos/XanaduAI/pennylane/statuses/{sha}","languages_url":"https://api.github.com/repos/XanaduAI/pennylane/languages","stargazers_url":"https://api.github.com/repos/XanaduAI/pennylane/stargazers","contributors_url":"https://api.github.com/repos/XanaduAI/pennylane/contributors","subscribers_url":"https://api.github.com/repos/XanaduAI/pennylane/subscribers","subscription_url":"https://api.github.com/repos/XanaduAI/pennylane/subscription","commits_url":"https://api.github.com/repos/XanaduAI/pennylane/commits{/sha}","git_commits_url":"https://api.github.com/repos/XanaduAI/pennylane/git/commits{/sha}","comments_url":"https://api.github.com/repos/XanaduAI/pennylane/comments{/number}","issue_comment_url":"https://api.github.com/repos/XanaduAI/pennylane/issues/comments{/number}","contents_url":"https://api.github.com/repos/XanaduAI/pennylane/contents/{+path}","compare_url":"https://api.github.com/repos/XanaduAI/pennylane/compare/{base}...{head}","merges_url":"https://api.github.com/repos/XanaduAI/pennylane/merges","archive_url":"https://api.github.com/repos/XanaduAI/pennylane/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/XanaduAI/pennylane/downloads","issues_url":"https://api.github.com/repos/XanaduAI/pennylane/issues{/number}","pulls_url":"https://api.github.com/repos/XanaduAI/pennylane/pulls{/number}","milestones_url":"https://api.github.com/repos/XanaduAI/pennylane/milestones{/number}","notifications_url":"https://api.github.com/repos/XanaduAI/pennylane/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/XanaduAI/pennylane/labels{/name}","releases_url":"https://api.github.com/repos/XanaduAI/pennylane/releases{/id}","deployments_url":"https://api.github.com/repos/XanaduAI/pennylane/deployments","created_at":"2018-04-17T16:45:42Z","updated_at":"2019-11-05T22:50:35Z","pushed_at":"2019-11-05T23:52:06Z","git_url":"git://github.com/XanaduAI/pennylane.git","ssh_url":"git@github.com:XanaduAI/pennylane.git","clone_url":"https://github.com/XanaduAI/pennylane.git","svn_url":"https://github.com/XanaduAI/pennylane","homepage":"https://pennylane.ai","size":23144,"stargazers_count":372,"watchers_count":372,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":92,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":27,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":92,"open_issues":27,"watchers":372,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/XanaduAI/pennylane/pulls/398"},"html":{"href":"https://github.com/XanaduAI/pennylane/pull/398"},"issue":{"href":"https://api.github.com/repos/XanaduAI/pennylane/issues/398"},"comments":{"href":"https://api.github.com/repos/XanaduAI/pennylane/issues/398/comments"},"review_comments":{"href":"https://api.github.com/repos/XanaduAI/pennylane/pulls/398/comments"},"review_comment":{"href":"https://api.github.com/repos/XanaduAI/pennylane/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/XanaduAI/pennylane/pulls/398/commits"},"statuses":{"href":"https://api.github.com/repos/XanaduAI/pennylane/statuses/b7b39134b3939cb9af456cf4b8303b67eed42e0c"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"co9olguy"}}