{"action":"created","comment":{"url":"https://api.github.com/repos/odlgroup/odl/pulls/comments/46758364","id":46758364,"diff_hunk":"@@ -25,51 +25,254 @@\n \n # External imports\n import numpy as np\n+from functools import wraps\n+from inspect import isclass, isfunction\n+from itertools import product\n \n # ODL imports\n-from odl.operator.operator import Operator\n-from odl.set.domain import IntervalProd\n-from odl.set.sets import RealNumbers, ComplexNumbers, Set\n+from odl.operator.operator import Operator, _dispatch_call_args\n+from odl.set.sets import RealNumbers, ComplexNumbers, Set, Field\n from odl.set.space import LinearSpace, LinearSpaceVector\n+from odl.util.utility import (is_valid_input_array, is_valid_input_meshgrid,\n+                              meshgrid_input_order, vecs_from_meshgrid)\n \n \n __all__ = ('FunctionSet', 'FunctionSetVector',\n            'FunctionSpace', 'FunctionSpaceVector')\n \n \n+def _out_of_place_not_impl(x, **kwargs):\n+    \"\"\"Dummy function used when out-of-place function is not given.\"\"\"\n+    raise NotImplementedError('no out-of-place evaluation method defined.')\n+\n+\n+def _default_in_place(func, x, out, **kwargs):\n+    \"\"\"Default in-place evaluation method.\"\"\"\n+    out[:] = func(x, **kwargs)\n+    return out\n+\n+\n+def _default_out_of_place(func, dtype, x, **kwargs):\n+    \"\"\"Default in-place evaluation method.\"\"\"\n+    if is_valid_input_array(x, func.domain.ndim):\n+        if func.domain.ndim == 1:\n+            if x.ndim == 2:\n+                x = x[0]\n+            out_shape = x.shape\n+        else:\n+            out_shape = (x.shape[1],)\n+    elif is_valid_input_meshgrid(x, func.domain.ndim):\n+        # Broadcasting fails for only one vector (ndim == 1)\n+        if func.domain.ndim == 1:\n+            x = x[0]\n+            out_shape = x.shape\n+        else:\n+            out_shape = np.broadcast(*x).shape\n+    else:\n+        raise TypeError('cannot use in-place method to implement '\n+                        'out-of-place non-vectorized evaluation.')\n+\n+    out = np.empty(out_shape, dtype=dtype)\n+    func(x, out=out, **kwargs)\n+    return out\n+\n+\n+def vectorize(dtype, outarg='none'):\n+    \"\"\"Vectorization decorator for our input parameter pattern.\n+\n+    The wrapped function must be callable with one positional\n+    parameter. Keyword arguments are passed through, hence positional\n+    arguments with defaults can either be left out or passed by keyword,\n+    but not by position.\n+\n+    Parameters\n+    ----------\n+    dtype : object\n+        Data type of the output array. Needs to be understood by the\n+        `numpy.dtype` function.\n+    outarg : {'none', 'positional', 'optional'}\n+        Type of the output argument of the decorated function for\n+        in-place evaluation\n+\n+        'none': No output parameter. This is the default.\n+        Resulting argspec: ``func(x, **kwargs)``\n+        Returns: the new array\n+\n+        'positional': Required argument ``out`` at second position.\n+        Resulting argspec: ``func(x, out=None, **kwargs)``\n+        Returns: ``out``\n+\n+        'optional': optional argument ``out`` with default `None`.\n+        Resulting argspec: ``func(x, out=None, **kwargs)``\n+        Returns: ``out`` if it is not `None` otherwise a new array\n+\n+    Note\n+    ----\n+    For ``outarg`` not equal to 'none', the decorated function returns\n+    the array given as ``out`` argument if it is not `None`.\n+\n+    Examples\n+    --------\n+    Vectorize a step function in the first variable:\n+\n+    >>> @vectorize(dtype=float)\n+    ... def step(x):\n+    ...     return 0 if x[0] <= 0 else 1\n+\n+    This corresponds to (but is much slower than)\n+\n+    >>> import numpy as np\n+    >>> def step_vec(x):\n+    ...     x0, x1 = x\n+    ...     # np.broadcast is your friend to determine the output shape\n+    ...     out = np.zeros(np.broadcast(x0, x1).shape, dtype=x0.dtype)\n+    ...     idcs = np.where(x0 > 0)\n+    ...     # Need to throw away the indices from the empty dimensions\n+    ...     idcs = idcs[0] if len(idcs) > 1 else idcs\n+    ...     out[idcs] = 1\n+    ...     return out\n+\n+    Both versions work for arrays and meshgrids:\n+\n+    >>> x = np.linspace(-5, 13, 10, dtype=float).reshape((2, 5))\n+    >>> x  # array representing 5 points in 2d\n+    array([[ -5.,  -3.,  -1.,   1.,   3.],\n+           [  5.,   7.,   9.,  11.,  13.]])\n+    >>> np.array_equal(step(x), step_vec(x))\n+    True\n+\n+    >>> x = y = np.linspace(-1, 2, 5)\n+    >>> mg_sparse = np.meshgrid(x, y, indexing='ij', sparse=True)\n+    >>> np.array_equal(step(mg_sparse), step_vec(mg_sparse))\n+    True\n+    >>> mg_dense = np.meshgrid(x, y, indexing='ij', sparse=False)\n+    >>> np.array_equal(step(mg_dense), step_vec(mg_dense))\n+    True\n+\n+    With output parameter:\n+\n+    >>> @vectorize(dtype=float, outarg='positional')\n+    ... def step(x):\n+    ...     return 0 if x[0] <= 0 else 1\n+    >>> x = np.linspace(-5, 13, 10, dtype=float).reshape((2, 5))\n+    >>> out = np.empty(5, dtype=float)\n+    >>> step(x, out)  # returns out\n+    array([ 0.,  0.,  0.,  1.,  1.])\n+    \"\"\"\n+    def vect_decorator(func):\n+        def _vect_wrapper(x, out, **kwargs):\n+            if isinstance(x, np.ndarray):  # array\n+                if x.ndim == 1:\n+                    dim = 1\n+                elif x.ndim == 2:\n+                    dim = len(x)\n+                    if dim == 1:\n+                        x = x.squeeze()\n+                else:\n+                    raise ValueError('only 1- or 2-dimensional arrays '\n+                                     'supported.')\n+            else:  # meshgrid\n+                dim = len(x)\n+                if dim == 1:\n+                    x = x[0]\n+            if dim == 1:\n+                if out is None:\n+                    out = np.empty(x.size, dtype=dtype)\n+                for i, xi in enumerate(x):\n+                    out[i] = func(xi, **kwargs)\n+                return out\n+            else:\n+                if is_valid_input_array(x, dim):\n+                    if out is None:\n+                        out = np.empty(x.shape[1], dtype=dtype)\n+                    for i, pt in enumerate(x.T):\n+                        out[i] = func(pt, **kwargs)\n+                    return out\n+                elif is_valid_input_meshgrid(x, dim):\n+                    order = meshgrid_input_order(x)\n+                    if out is None:\n+                        out_shape = np.broadcast(*x).shape\n+                        out = np.empty(out_shape, dtype=dtype, order=order)\n+\n+                    # TODO: find a better way to iterate\n+                    vecs = vecs_from_meshgrid(x, order=order)\n+                    for i, pt in enumerate(product(*vecs)):\n+                        out.flat[i] = func(pt, **kwargs)\n+                    return out\n+                else:\n+                    if out is None:\n+                        try:\n+                            return func(x)\n+                        except (TypeError, ValueError, IndexError):\n+                            raise TypeError('invalid vectorized input type.')\n+                    else:\n+                        raise TypeError('invalid vectorized input type.')\n+\n+        @wraps(func)\n+        def vect_wrapper_no_out(x, **kwargs):\n+            if 'out' in kwargs:\n+                raise TypeError(\"{}() got an unexpected keyword 'out'.\"\n+                                \"\".format(func.__name__))\n+            return _vect_wrapper(x, None, **kwargs)\n+\n+        @wraps(func)\n+        def vect_wrapper_pos_out(x, out, **kwargs):\n+            if out is None:\n+                raise ValueError('output parameter cannot be `None`.')\n+            return _vect_wrapper(x, out, **kwargs)\n+\n+        @wraps(func)\n+        def vect_wrapper_opt_out(x, out=None, **kwargs):\n+            return _vect_wrapper(x, out, **kwargs)\n+\n+        outarg_ = str(outarg).lower()","path":"odl/space/fspace.py","position":207,"original_position":207,"commit_id":"ad630b9ac13caf9440f1d9d77acbaa9d28996a06","original_commit_id":"ad630b9ac13caf9440f1d9d77acbaa9d28996a06","user":{"login":"adler-j","id":2202312,"avatar_url":"https://avatars.githubusercontent.com/u/2202312?v=3","gravatar_id":"","url":"https://api.github.com/users/adler-j","html_url":"https://github.com/adler-j","followers_url":"https://api.github.com/users/adler-j/followers","following_url":"https://api.github.com/users/adler-j/following{/other_user}","gists_url":"https://api.github.com/users/adler-j/gists{/gist_id}","starred_url":"https://api.github.com/users/adler-j/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adler-j/subscriptions","organizations_url":"https://api.github.com/users/adler-j/orgs","repos_url":"https://api.github.com/users/adler-j/repos","events_url":"https://api.github.com/users/adler-j/events{/privacy}","received_events_url":"https://api.github.com/users/adler-j/received_events","type":"User","site_admin":false},"body":"why the _? Usually we do this \"in place\"","created_at":"2015-12-05T13:32:54Z","updated_at":"2015-12-05T13:32:54Z","html_url":"https://github.com/odlgroup/odl/pull/116#discussion_r46758364","pull_request_url":"https://api.github.com/repos/odlgroup/odl/pulls/116","_links":{"self":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/comments/46758364"},"html":{"href":"https://github.com/odlgroup/odl/pull/116#discussion_r46758364"},"pull_request":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/116"}}},"pull_request":{"url":"https://api.github.com/repos/odlgroup/odl/pulls/116","id":52563652,"html_url":"https://github.com/odlgroup/odl/pull/116","diff_url":"https://github.com/odlgroup/odl/pull/116.diff","patch_url":"https://github.com/odlgroup/odl/pull/116.patch","issue_url":"https://api.github.com/repos/odlgroup/odl/issues/116","number":116,"state":"open","locked":false,"title":"Issue 57  operator","user":{"login":"kohr-h","id":5030250,"avatar_url":"https://avatars.githubusercontent.com/u/5030250?v=3","gravatar_id":"","url":"https://api.github.com/users/kohr-h","html_url":"https://github.com/kohr-h","followers_url":"https://api.github.com/users/kohr-h/followers","following_url":"https://api.github.com/users/kohr-h/following{/other_user}","gists_url":"https://api.github.com/users/kohr-h/gists{/gist_id}","starred_url":"https://api.github.com/users/kohr-h/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kohr-h/subscriptions","organizations_url":"https://api.github.com/users/kohr-h/orgs","repos_url":"https://api.github.com/users/kohr-h/repos","events_url":"https://api.github.com/users/kohr-h/events{/privacy}","received_events_url":"https://api.github.com/users/kohr-h/received_events","type":"User","site_admin":false},"body":"Tests are all running through now (except PEP8), but need to write some tests beyond the current pure doctests. This is just a preliminary PR but may be worth a  look at. I'll write a more comprehensive summary later.","created_at":"2015-12-03T17:13:34Z","updated_at":"2015-12-05T13:32:54Z","closed_at":null,"merged_at":null,"merge_commit_sha":"577104a6092b8d561deccc423f90ee8b4e77c7eb","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/odlgroup/odl/pulls/116/commits","review_comments_url":"https://api.github.com/repos/odlgroup/odl/pulls/116/comments","review_comment_url":"https://api.github.com/repos/odlgroup/odl/pulls/comments{/number}","comments_url":"https://api.github.com/repos/odlgroup/odl/issues/116/comments","statuses_url":"https://api.github.com/repos/odlgroup/odl/statuses/ad630b9ac13caf9440f1d9d77acbaa9d28996a06","head":{"label":"odlgroup:issue-57__operator","ref":"issue-57__operator","sha":"ad630b9ac13caf9440f1d9d77acbaa9d28996a06","user":{"login":"odlgroup","id":15667171,"avatar_url":"https://avatars.githubusercontent.com/u/15667171?v=3","gravatar_id":"","url":"https://api.github.com/users/odlgroup","html_url":"https://github.com/odlgroup","followers_url":"https://api.github.com/users/odlgroup/followers","following_url":"https://api.github.com/users/odlgroup/following{/other_user}","gists_url":"https://api.github.com/users/odlgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/odlgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odlgroup/subscriptions","organizations_url":"https://api.github.com/users/odlgroup/orgs","repos_url":"https://api.github.com/users/odlgroup/repos","events_url":"https://api.github.com/users/odlgroup/events{/privacy}","received_events_url":"https://api.github.com/users/odlgroup/received_events","type":"Organization","site_admin":false},"repo":{"id":45596393,"name":"odl","full_name":"odlgroup/odl","owner":{"login":"odlgroup","id":15667171,"avatar_url":"https://avatars.githubusercontent.com/u/15667171?v=3","gravatar_id":"","url":"https://api.github.com/users/odlgroup","html_url":"https://github.com/odlgroup","followers_url":"https://api.github.com/users/odlgroup/followers","following_url":"https://api.github.com/users/odlgroup/following{/other_user}","gists_url":"https://api.github.com/users/odlgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/odlgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odlgroup/subscriptions","organizations_url":"https://api.github.com/users/odlgroup/orgs","repos_url":"https://api.github.com/users/odlgroup/repos","events_url":"https://api.github.com/users/odlgroup/events{/privacy}","received_events_url":"https://api.github.com/users/odlgroup/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/odlgroup/odl","description":"Operator Discretization Library","fork":false,"url":"https://api.github.com/repos/odlgroup/odl","forks_url":"https://api.github.com/repos/odlgroup/odl/forks","keys_url":"https://api.github.com/repos/odlgroup/odl/keys{/key_id}","collaborators_url":"https://api.github.com/repos/odlgroup/odl/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/odlgroup/odl/teams","hooks_url":"https://api.github.com/repos/odlgroup/odl/hooks","issue_events_url":"https://api.github.com/repos/odlgroup/odl/issues/events{/number}","events_url":"https://api.github.com/repos/odlgroup/odl/events","assignees_url":"https://api.github.com/repos/odlgroup/odl/assignees{/user}","branches_url":"https://api.github.com/repos/odlgroup/odl/branches{/branch}","tags_url":"https://api.github.com/repos/odlgroup/odl/tags","blobs_url":"https://api.github.com/repos/odlgroup/odl/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/odlgroup/odl/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/odlgroup/odl/git/refs{/sha}","trees_url":"https://api.github.com/repos/odlgroup/odl/git/trees{/sha}","statuses_url":"https://api.github.com/repos/odlgroup/odl/statuses/{sha}","languages_url":"https://api.github.com/repos/odlgroup/odl/languages","stargazers_url":"https://api.github.com/repos/odlgroup/odl/stargazers","contributors_url":"https://api.github.com/repos/odlgroup/odl/contributors","subscribers_url":"https://api.github.com/repos/odlgroup/odl/subscribers","subscription_url":"https://api.github.com/repos/odlgroup/odl/subscription","commits_url":"https://api.github.com/repos/odlgroup/odl/commits{/sha}","git_commits_url":"https://api.github.com/repos/odlgroup/odl/git/commits{/sha}","comments_url":"https://api.github.com/repos/odlgroup/odl/comments{/number}","issue_comment_url":"https://api.github.com/repos/odlgroup/odl/issues/comments{/number}","contents_url":"https://api.github.com/repos/odlgroup/odl/contents/{+path}","compare_url":"https://api.github.com/repos/odlgroup/odl/compare/{base}...{head}","merges_url":"https://api.github.com/repos/odlgroup/odl/merges","archive_url":"https://api.github.com/repos/odlgroup/odl/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/odlgroup/odl/downloads","issues_url":"https://api.github.com/repos/odlgroup/odl/issues{/number}","pulls_url":"https://api.github.com/repos/odlgroup/odl/pulls{/number}","milestones_url":"https://api.github.com/repos/odlgroup/odl/milestones{/number}","notifications_url":"https://api.github.com/repos/odlgroup/odl/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/odlgroup/odl/labels{/name}","releases_url":"https://api.github.com/repos/odlgroup/odl/releases{/id}","created_at":"2015-11-05T07:57:55Z","updated_at":"2015-11-10T16:59:15Z","pushed_at":"2015-12-04T19:21:38Z","git_url":"git://github.com/odlgroup/odl.git","ssh_url":"git@github.com:odlgroup/odl.git","clone_url":"https://github.com/odlgroup/odl.git","svn_url":"https://github.com/odlgroup/odl","homepage":null,"size":12888,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":47,"forks":1,"open_issues":47,"watchers":0,"default_branch":"master"}},"base":{"label":"odlgroup:master","ref":"master","sha":"310c743eaeeb0d6c2451f70ba273065022ae49e3","user":{"login":"odlgroup","id":15667171,"avatar_url":"https://avatars.githubusercontent.com/u/15667171?v=3","gravatar_id":"","url":"https://api.github.com/users/odlgroup","html_url":"https://github.com/odlgroup","followers_url":"https://api.github.com/users/odlgroup/followers","following_url":"https://api.github.com/users/odlgroup/following{/other_user}","gists_url":"https://api.github.com/users/odlgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/odlgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odlgroup/subscriptions","organizations_url":"https://api.github.com/users/odlgroup/orgs","repos_url":"https://api.github.com/users/odlgroup/repos","events_url":"https://api.github.com/users/odlgroup/events{/privacy}","received_events_url":"https://api.github.com/users/odlgroup/received_events","type":"Organization","site_admin":false},"repo":{"id":45596393,"name":"odl","full_name":"odlgroup/odl","owner":{"login":"odlgroup","id":15667171,"avatar_url":"https://avatars.githubusercontent.com/u/15667171?v=3","gravatar_id":"","url":"https://api.github.com/users/odlgroup","html_url":"https://github.com/odlgroup","followers_url":"https://api.github.com/users/odlgroup/followers","following_url":"https://api.github.com/users/odlgroup/following{/other_user}","gists_url":"https://api.github.com/users/odlgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/odlgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odlgroup/subscriptions","organizations_url":"https://api.github.com/users/odlgroup/orgs","repos_url":"https://api.github.com/users/odlgroup/repos","events_url":"https://api.github.com/users/odlgroup/events{/privacy}","received_events_url":"https://api.github.com/users/odlgroup/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/odlgroup/odl","description":"Operator Discretization Library","fork":false,"url":"https://api.github.com/repos/odlgroup/odl","forks_url":"https://api.github.com/repos/odlgroup/odl/forks","keys_url":"https://api.github.com/repos/odlgroup/odl/keys{/key_id}","collaborators_url":"https://api.github.com/repos/odlgroup/odl/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/odlgroup/odl/teams","hooks_url":"https://api.github.com/repos/odlgroup/odl/hooks","issue_events_url":"https://api.github.com/repos/odlgroup/odl/issues/events{/number}","events_url":"https://api.github.com/repos/odlgroup/odl/events","assignees_url":"https://api.github.com/repos/odlgroup/odl/assignees{/user}","branches_url":"https://api.github.com/repos/odlgroup/odl/branches{/branch}","tags_url":"https://api.github.com/repos/odlgroup/odl/tags","blobs_url":"https://api.github.com/repos/odlgroup/odl/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/odlgroup/odl/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/odlgroup/odl/git/refs{/sha}","trees_url":"https://api.github.com/repos/odlgroup/odl/git/trees{/sha}","statuses_url":"https://api.github.com/repos/odlgroup/odl/statuses/{sha}","languages_url":"https://api.github.com/repos/odlgroup/odl/languages","stargazers_url":"https://api.github.com/repos/odlgroup/odl/stargazers","contributors_url":"https://api.github.com/repos/odlgroup/odl/contributors","subscribers_url":"https://api.github.com/repos/odlgroup/odl/subscribers","subscription_url":"https://api.github.com/repos/odlgroup/odl/subscription","commits_url":"https://api.github.com/repos/odlgroup/odl/commits{/sha}","git_commits_url":"https://api.github.com/repos/odlgroup/odl/git/commits{/sha}","comments_url":"https://api.github.com/repos/odlgroup/odl/comments{/number}","issue_comment_url":"https://api.github.com/repos/odlgroup/odl/issues/comments{/number}","contents_url":"https://api.github.com/repos/odlgroup/odl/contents/{+path}","compare_url":"https://api.github.com/repos/odlgroup/odl/compare/{base}...{head}","merges_url":"https://api.github.com/repos/odlgroup/odl/merges","archive_url":"https://api.github.com/repos/odlgroup/odl/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/odlgroup/odl/downloads","issues_url":"https://api.github.com/repos/odlgroup/odl/issues{/number}","pulls_url":"https://api.github.com/repos/odlgroup/odl/pulls{/number}","milestones_url":"https://api.github.com/repos/odlgroup/odl/milestones{/number}","notifications_url":"https://api.github.com/repos/odlgroup/odl/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/odlgroup/odl/labels{/name}","releases_url":"https://api.github.com/repos/odlgroup/odl/releases{/id}","created_at":"2015-11-05T07:57:55Z","updated_at":"2015-11-10T16:59:15Z","pushed_at":"2015-12-04T19:21:38Z","git_url":"git://github.com/odlgroup/odl.git","ssh_url":"git@github.com:odlgroup/odl.git","clone_url":"https://github.com/odlgroup/odl.git","svn_url":"https://github.com/odlgroup/odl","homepage":null,"size":12888,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":47,"forks":1,"open_issues":47,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/116"},"html":{"href":"https://github.com/odlgroup/odl/pull/116"},"issue":{"href":"https://api.github.com/repos/odlgroup/odl/issues/116"},"comments":{"href":"https://api.github.com/repos/odlgroup/odl/issues/116/comments"},"review_comments":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/116/comments"},"review_comment":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/116/commits"},"statuses":{"href":"https://api.github.com/repos/odlgroup/odl/statuses/ad630b9ac13caf9440f1d9d77acbaa9d28996a06"}}}}