-
Notifications
You must be signed in to change notification settings - Fork 5.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(ivy/functional): update diagflat docstring #23342
Closed
Closed
Changes from 39 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
398f178
fixed a bug, I modify the code to handle the case when dim is 1 sepa…
shephinphilip 0e72bb5
fixed a bug, I modify the code to handle the case when dim is 1 sepa…
shephinphilip 5ee8d58
fixed a bug, I modify the code to handle the case when dim is 1 sepa…
shephinphilip 78fb315
Fixing the utils init file
shephinphilip adb130f
Fixing the utils init file
shephinphilip 5447027
Fixed the 'unknown_shape not defined' error
shephinphilip 53dfa84
Fixed the 'unknown_shape not defined' error
shephinphilip 9aac507
ivy.utils
shephinphilip e510faa
ivy.utils
shephinphilip 5ae79ff
ivy.utils
shephinphilip a86dc38
ivy.utils
shephinphilip db95c76
ivy.utils
shephinphilip ef13702
added docs folder
shephinphilip 69c1e2b
Copy exact code from master branch to previous-branch
shephinphilip ecb4c17
Solved ivy utils
shephinphilip 64a3d44
Merge branch 'master' of https://github.com/shephinphilip/ivy into Iv…
shephinphilip 71c877e
Changed open_task
shephinphilip 8582e11
Merge branch 'master' into Doc-opentask
shephinphilip 1eeff65
made changes in ivy/__init__.py
shephinphilip 4556e75
Merge branch 'Doc-opentask' of https://github.com/shephinphilip/ivy i…
shephinphilip 2a73b10
changed to '
shephinphilip 6f9e14c
/array_api_testing/test_array_api.diff
shephinphilip de97d4d
Merge branch 'master' into Doc-opentask
shephinphilip 83f9be1
changes in docs
shephinphilip 1b94167
commented the code
shephinphilip e0a0f4b
created conf file
shephinphilip ba4aa28
Merge remote-tracking branch 'upstream/master' into Doc-conf
shephinphilip 0470c21
feat(docs/api-standards): fix placeholders in api_standards.rst
shephinphilip 52527fe
Merge branch 'master' of https://github.com/shephinphilip/ivy
shephinphilip 1aa280d
Merge branch 'master' into docs/api-standards
shephinphilip ab453c6
avoid conflict
shephinphilip ae529b0
updated diagflat docstring
shephinphilip c3f5950
Merge branch 'main' into ivy-functional
shephinphilip 722e228
deleted conf.py
shephinphilip 02e656a
Merge branch 'ivy-functional' of https://github.com/shephinphilip/ivy…
shephinphilip e970508
changed some files
shephinphilip a6fa1cf
changed some files
shephinphilip d0176c3
changed some files
shephinphilip 57d9c21
Merge branch 'ivy-functional' of https://github.com/shephinphilip/ivy…
shephinphilip e309678
changes made in file
shephinphilip b17c475
Your commit message here
shephinphilip 749d58e
Merge remote-tracking branch 'upstream/main' into ivy-functional
shephinphilip 86b2033
made some changes
shephinphilip 6208e5d
Merge branch 'ivy-functional' of https://github.com/shephinphilip/ivy…
shephinphilip b7ffb01
made some changes
shephinphilip 06db220
Your commit message here
shephinphilip 4bf73d7
changed in diagflow
shephinphilip 0c4eb52
changed in ivy/__init__.py
shephinphilip cdce808
Update linear_algebra.py
shephinphilip 7512af2
🤖 Lint code
ivy-branch f99183c
Update linear_algebra.py
shephinphilip File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,10 +22,11 @@ Arrays | |
.. _`repo`: https://github.com/unifyai/ivy | ||
.. _`discord`: https://discord.gg/sXyFF8tDtm | ||
.. _`arrays channel`: https://discord.com/channels/799879767196958751/933380487353872454 | ||
.. _`arrays forum`: https://discord.com/channels/799879767196958751/1028296936203235359 | ||
.. _`wrapped logic`: https://github.com/unifyai/ivy/blob/6a729004c5e0db966412b00aa2fce174482da7dd/ivy/func_wrapper.py#L95 | ||
.. _`NumPy's`: https://numpy.org/doc/stable/user/basics.dispatch.html#basics-dispatch | ||
.. _`PyTorch's`: https://pytorch.org/docs/stable/notes/extending.html#extending-torch | ||
There are two types of arrays in Ivy, there is the :class:`ivy.NativeArray` and also the :class:`ivy.Array`. | ||
There are two types of array in Ivy, there is the :class:`ivy.NativeArray` and also the :class:`ivy.Array`. | ||
|
||
Native Array | ||
------------ | ||
|
@@ -44,7 +45,7 @@ All functions in the Ivy functional API which accept *at least one array argumen | |
The only exceptions to this are functions in the `nest <https://github.com/unifyai/ivy/blob/906ddebd9b371e7ae414cdd9b4bf174fd860efc0/ivy/functional/ivy/nest.py>`_ module and the `meta <https://github.com/unifyai/ivy/blob/906ddebd9b371e7ae414cdd9b4bf174fd860efc0/ivy/functional/ivy/meta.py>`_ module, which have no instance method implementations. | ||
|
||
The organization of these instance methods follows the same organizational structure as the files in the functional API. | ||
The :class:`ivy.Array` class `inherits`_ from many category-specific array classes, such as `ArrayWithElementwise`_, each of which implements the category-specific instance methods. | ||
The :class:`ivy.Array` class `inherits`_ from many category-specific array classes, such as `ArrayWithElementwise`_, each of which implement the category-specific instance methods. | ||
|
||
Each instance method simply calls the functional API function internally, but passes in :code:`self._data` as the first *array* argument. | ||
`ivy.Array.add`_ is a good example. | ||
|
@@ -85,19 +86,19 @@ Therefore, most functions in Ivy must adopt the following pipeline: | |
#. call the backend-specific function, passing in these :class:`ivy.NativeArray` instances | ||
#. convert all of the :class:`ivy.NativeArray` instances which are returned from the backend function back into :class:`ivy.Array` instances, and return | ||
|
||
Given the repeating nature of these steps, this is all entirely handled in the `inputs_to_native_arrays`_ and `outputs_to_ivy_arrays`_ wrappers, as explained in the `Function Wrapping <function_wrapping.rst>`_ section. | ||
Given the repeating nature of these steps, this is all entirely handled in the `inputs_to_native_arrays`_ and `outputs_to_ivy_arrays`_ wrappers, as explained in the :ref:`Function Wrapping` section. | ||
|
||
All Ivy functions *also* accept :class:`ivy.NativeArray` instances in the input. | ||
This is for a couple of reasons. | ||
Firstly, :class:`ivy.Array` instances must be converted to :class:`ivy.NativeArray` instances anyway, and so supporting them in the input is not a problem. | ||
Secondly, this makes it easier to combine backend-specific code with Ivy code, without needing to explicitly wrap any arrays before calling sections of Ivy code. | ||
|
||
Therefore, all input arrays to Ivy functions have type :code:`Union[ivy.Array, ivy.NativeArray]`, whereas the output arrays have type :class:`ivy.Array`. | ||
This is further explained in the `Function Arguments <function_arguments.rst>`_ section. | ||
This is further explained in the :ref:`Function Arguments` section. | ||
|
||
However, :class:`ivy.NativeArray` instances are not permitted for the :code:`out` argument, which is used in most functions. | ||
This is because the :code:`out` argument dictates the array to which the result should be written, and so it effectively serves the same purpose as the function return. | ||
This is further explained in the `Inplace Updates <inplace_updates.rst>`_ section. | ||
This is further explained in the :ref:`Inplace Updates` section. | ||
|
||
As a final point, extra attention is required for *compositional* functions, as these do not directly defer to a backend implementation. | ||
If the first line of code in a compositional function performs operations on the input array, then this will call the special methods on an :class:`ivy.NativeArray` and not on an :class:`ivy.Array`. | ||
|
@@ -113,7 +114,7 @@ Ivy's functional API and its functions can easily be integrated with non-Ivy cla | |
To make use of that feature, the class must contain an implementation for these functions and it must contain an implementation for the function :code:`__ivy_array_function__`. If a non-Ivy class is passed to an Ivy function, a call to this class's :code:`__ivy_array_function__` is made which directs Ivy's function to handle that input type correctly. This allows users to define custom implementations for any of the functions that can be found in Ivy's functional API which would further make it easy to integrate those classes with other Ivy projects. | ||
|
||
**Note** | ||
This functionality is inspired by `NumPy's`_ :code:`__ivy_array_function__` and `PyTorch's`_ :code:`__torch_function__`. | ||
This functionality is inspired by `NumPy's`_ :code:`__ivy_array_function__` and `PyTorch's`_ :code:`__torch_function__`. | ||
|
||
As an example, consider the following class :code:`MyArray` with the following definition: | ||
|
||
|
@@ -136,22 +137,22 @@ There are different ways to do so. One way is to use a global dict :code:`HANDLE | |
def __ivy_array_function__(self, func, types, args, kwargs): | ||
if func not in HANDLED_FUNCTIONS: | ||
return NotImplemented | ||
if not all(issubclass(t, (MyArray, ivy.Array, ivy.NativeArray)) for t in types): | ||
if not all((t, (MyArray, ivy.Array, ivy.NativeArray)) for t in types): | ||
return NotImplemented | ||
return HANDLED_FUNCTIONS[func](*args, **kwargs) | ||
return HANDLED_FUNCTIONS[func](*args, **kwargs) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think these extra spaces are required, similarly for other places. |
||
|
||
:code:`__ivy_array_function__` accepts four parameters: :code:`func` representing a reference to the array API function being | ||
:code:`__ivy_array_function__` accepts four parameters: :code:`func` representing a reference to the array API function being | ||
overridden, :code:`types` a list of the types of objects implementing :code:`__ivy_array_function__`, :code:`args` a tuple of arguments supplied to the function, and :code:`kwargs` being a dictionary of keyword arguments passed to the function. | ||
While this class contains an implementation for :code:`__ivy_array_function__`, it is still not enough as it is necessary to implement any needed Ivy functions with the new :code:`MyArray` class as input(s) for the code to run successfully. | ||
We will define a decorator function :code:`implements` that can be used to add functions to :code:`HANDLED_FUNCTIONS`: | ||
We will define a decorator function :code:`implements` that can be used to add functions to :code:`HANDLED_FUNCTIONS`: | ||
|
||
.. code-block:: python | ||
|
||
def implements(ivy_function): | ||
def decorator(func): | ||
HANDLED_FUNCTIONS[ivy_function] = func | ||
return func | ||
return decorator | ||
return decorator | ||
|
||
Lastly, we need to apply that decorator to the override function. Let’s consider for example a function that overrides :code:`ivy.abs`: | ||
|
||
|
@@ -168,15 +169,15 @@ Now that we have added the function to :code:`HANDLED_FUNCTIONS`, we can now use | |
X = MyArray(-3) | ||
X = ivy.abs(X) | ||
|
||
Of course :code:`ivy.abs` is an example of a function that is easy to override since it only requires one operand. The same approach can be used to override functions with multiple operands, including arrays or array-like objects that define :code:`__ivy_array_function__`. | ||
Of course :code:`ivy.abs` is an example of a function that is easy to override since it only requires one operand. The same approach can be used to override functions with multiple operands, including arrays or array-like objects that define :code:`__ivy_array_function__`. | ||
|
||
It is relevant to mention again that any function not stored inside the dict :code:`HANDLED_FUNCTIONS` will not work and it is also important to notice that the operands passed to the function must match that of the function stored in the dict. For instance :code:`my_abs` takes only one parameter which is a :code:`MyArray` object. So, passing any other operands to the function will result in an exception :code:`IvyBackendException` being thrown. Lastly, for a custom class to be covered completely with Ivy's functional API, it is necessary to create an implementation for all the relevant functions within the API that will be used by this custom class. That can be all the functions in the API or only a subset of them. | ||
|
||
**Round Up** | ||
|
||
This should have hopefully given you a good feel for the different types of arrays, and how these are handled in Ivy. | ||
|
||
If you have any questions, please feel free to reach out on `discord`_ in the `arrays channel`_! | ||
If you have any questions, please feel free to reach out on `discord`_ in the `arrays channel`_ or in the `arrays forum`_! | ||
|
||
|
||
**Video** | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ Continuous Integration | |
====================== | ||
|
||
.. _`continuous integration channel`: https://discord.com/channels/799879767196958751/1028268051776413759 | ||
.. _`continuous integration forum`: https://discord.com/channels/799879767196958751/1028298018438520862 | ||
.. _`discord`: https://discord.gg/sXyFF8tDtm | ||
|
||
We follow the practice of Continuous Integration (CI), in order to regularly build and test code at Ivy. | ||
|
@@ -16,10 +17,10 @@ In order to incorporate Continuous Integration in the Ivy Repository, we follow | |
#. Periodic Testing | ||
#. Manual Testing | ||
|
||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/main/img/externally_linked/deep_dive/continuous_integration/CI.png?raw=true | ||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/master/img/externally_linked/deep_dive/continuous_integration/CI.png?raw=true | ||
:alt: CI Overview | ||
|
||
We use GitHub Actions in order to implement and automate the process of testing. GitHub Actions allow implementing custom workflows that can build the code in the repository and run the tests. All the workflows used by Ivy are defined in the `.github/workflows <https://github.com/unifyai/ivy/tree/main/.github/workflows>`_ directory. | ||
We use GitHub Actions in order to implement and automate the process of testing. GitHub Actions allow implementing custom workflows that can build the code in the repository and run the tests. All the workflows used by Ivy are defined in the `.github/workflows <https://github.com/unifyai/ivy/tree/master/.github/workflows>`_ directory. | ||
|
||
Commit (Push/PR) Triggered Testing | ||
---------------------------------- | ||
|
@@ -36,7 +37,7 @@ A test is defined as the triplet of (submodule, function, backend). We follow th | |
|
||
For example, :code:`ivy_tests/test_ivy/test_frontends/test_torch/test_tensor.py::test_torch_instance_arctan_,numpy` | ||
|
||
The Number of such Ivy tests running on the Repository (without taking any Framework/Python Versioning into account) is 12500 (as of writing this documentation), and we are adding tests daily. Therefore, triggering all the tests on each commit is neither desirable (as it will consume a huge lot of Compute Resources, as well as take a large amount of time to run) nor feasible (as Each Job in Github Actions has a time Limit of 360 Minutes, and a Memory Limit as well). | ||
The Number of such Ivy tests running on the Repository (without taking any Framework/Python Versioning into account) is 12500 (as of writing this documentation), and we are adding tests daily. Therefore, triggering all the tests on each commit is neither desirable (as it will consume a huge lot of Compute Resources, as well take a large amount of time to run) nor feasible (as Each Job in Github Actions has a time Limit of 360 Minutes, and a Memory Limit as well). | ||
|
||
Further, When we consider versioning, for a single Python version, and ~40 frontend and backend versions, the tests would shoot up to 40 * 40 * 12500 = 20,000,000, and we obviously don't have resources as well as time to run those many tests on each commit. | ||
|
||
|
@@ -90,7 +91,7 @@ But, there’s a fundamental issue here, Computing the Mapping requires determin | |
|
||
Now assume that we had some way to update the Mapping for a commit from the previous Mapping without having to run all the tests. Then, Given the Mapping for a single commit, we could follow this to determine and run the relevant tests for each commit as follows: | ||
|
||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/main/img/externally_linked/deep_dive/continuous_integration/ITRoadmap.png?raw=true | ||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/master/img/externally_linked/deep_dive/continuous_integration/ITRoadmap.png?raw=true | ||
:alt: Intelligent Testing Roadmap | ||
This is exactly what we do in order to implement Intelligent Testing. The “Update Mapping” Logic works as follows for each changed file: | ||
|
||
|
@@ -206,7 +207,7 @@ Now, that the SSH key of the Runner has permissions to push and clone the Mappin | |
|
||
git clone --single-branch --depth 1 --branch "$TARGET_BRANCH" [email protected]:unifyai/Mapping.git | ||
|
||
In the case of, Pull Requests, we do not have access to :code:`SSH_DEPLOY_KEY` secret (and we don’t even want to give PRs that access), and thus we don’t use the SSH Clone Methodology and instead use the HTTP Clone Method, as follows: | ||
In case of, Pull Requests, we do not have access to :code:`SSH_DEPLOY_KEY` secret (and we don’t even want to give PRs that access), and thus we don’t use the SSH Clone Methodology and instead use the HTTP Clone Method, as follows: | ||
|
||
.. code-block:: | ||
|
||
|
@@ -273,7 +274,7 @@ The Determine Test Coverage Workflow and the Intelligent Tests Workflow can run | |
Consider the following Case for Runner 2: | ||
|
||
#. The Determine Test Coverage workflow has been running, and is about to complete for Runner 2. Meanwhile, a commit made on the master triggers the intelligent-tests workflow. | ||
#. The runner 2 in the intelligent-tests workflow, pulls the Mapping from the master2 branch of the unifyai/Mapping repository, and starts running the determined tests (based on changes made in the commit). | ||
#. The runner 2 in the intelligent-tests workflow, pulls the Mapping from the master2 branch of unifyai/Mapping repository, and starts running the determined tests (based on changes made in the commit). | ||
#. The det-test-coverage workflow completes for runner2, which makes a push to the corresponding branch in the unifyai/Mapping Repository. | ||
#. The runner 2 in the intelligent-tests workflow also completes, and pushes the updated repository | ||
|
||
|
@@ -287,14 +288,14 @@ We handle the Race Condition as follows: | |
|
||
Array API Tests | ||
--------------- | ||
The `array-api-intelligent-tests.yml (Push) <https://github.com/unifyai/ivy/blob/main/.github/workflows/array-api-intelligent-tests.yml>`_ and the `array-api-intelligent-tests-pr.yml (Pull Request) <https://github.com/unifyai/ivy/blob/main/.github/workflows/array-api-intelligent-tests-pr.yml>`_ workflows run the Array API Tests. Similar to Ivy Tests, The Array API tests are also determined intelligently and only relevant tests are triggered on each commit. | ||
The `array-api-intelligent-tests.yml (Push) <https://github.com/unifyai/ivy/blob/master/.github/workflows/array-api-intelligent-tests.yml>`_ and the `array-api-intelligent-tests-pr.yml (Pull Request) <https://github.com/unifyai/ivy/blob/master/.github/workflows/array-api-intelligent-tests-pr.yml>`_ workflows run the Array API Tests. Similar to Ivy Tests, The Array API tests are also determined intelligently and only relevant tests are triggered on each commit. | ||
|
||
More details about the Array API Tests are available `here <array_api_tests.rst>`_. | ||
More details about the Array API Tests are available `here <https://unify.ai/docs/ivy/overview/deep_dive/array_api_tests.html>`_. | ||
|
||
Periodic Testing | ||
---------------- | ||
In order to make sure that none of the Ivy Tests are left ignored for a long time, and to decouple the rate of testing to the rate of committing to the repository, we implement periodic testing on the Ivy Repository. | ||
The `Test Ivy Cron Workflow <https://github.com/unifyai/ivy/blob/main/.github/workflows/test-ivy-cron.yml>`_ is responsible for implementing this behavior by running Ivy tests every hour. In Each Run, It triggers 150 Ivy Tests, cycling through all of the tests. | ||
The `Test Ivy Cron Workflow <https://github.com/unifyai/ivy/blob/master/.github/workflows/test-ivy-cron.yml>`_ is responsible for implementing this behavior by running Ivy tests every hour. In Each Run, It triggers 150 Ivy Tests, cycling through all of the tests. | ||
This number of 150 is chosen in order to make sure that the Action completes in 1 hour most of the time. | ||
The Test Results update the corresponding cell on the Dashboards. | ||
|
||
|
@@ -330,18 +331,18 @@ Whenever a push is made to the repository, a variety of workflows are triggered | |
This can be seen on the GitHub Repository Page, with the commit message followed by a yellow dot, indicating that some workflows have been queued to run following this commit, as shown below: | ||
|
||
|
||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/main/img/externally_linked/deep_dive/continuous_integration/push.png?raw=true | ||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/master/img/externally_linked/deep_dive/continuous_integration/push.png?raw=true | ||
:alt: Push | ||
|
||
Clicking on the yellow dot (🟡) (which changes to a tick (✔) or cross (❌), when the tests have been completed) yields a view of the test-suite results as shown below: | ||
|
||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/main/img/externally_linked/deep_dive/continuous_integration/push1.png?raw=true | ||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/master/img/externally_linked/deep_dive/continuous_integration/push1.png?raw=true | ||
:alt: Test-Suite | ||
|
||
Click on the "Details" link corresponding to the failing tests, in order to identify the cause of the failure. | ||
It redirects to the Actions Tab, showing details of the failure, as shown below: | ||
|
||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/main/img/externally_linked/deep_dive/continuous_integration/push2.png?raw=true | ||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/master/img/externally_linked/deep_dive/continuous_integration/push2.png?raw=true | ||
:alt: Workflow Result | ||
|
||
Click on the "Run Tests" section in order to see the logs of the failing tests for Array API Tests. For Ivy Tests, head to the "Combined Test Results" Section of the display-test-results Job, which shows the Test Logs for each of the tests in the following format: | ||
|
@@ -380,7 +381,7 @@ Pull Request | |
In case of a pull request, the test suite is available on the Pull Request Page on Github, as shown below: | ||
|
||
|
||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/main/img/externally_linked/deep_dive/continuous_integration/pull-request1.png?raw=true | ||
.. image:: https://github.com/unifyai/unifyai.github.io/blob/master/img/externally_linked/deep_dive/continuous_integration/pull-request1.png?raw=true | ||
:alt: PR Test-Suite | ||
|
||
Clicking on the "Details" link redirects to the Action Log. | ||
|
@@ -391,14 +392,15 @@ As an added feature, the Intelligent Tests for PR Workflow has a section on "New | |
Dashboard | ||
--------- | ||
In order to view the status of the tests, at any point in time, we have implemented a dashboard application that shows the results of the latest Workflow that ran each test. | ||
The Dashboards are available at the link: https://ivy-dynamical-dashboards.onrender.com | ||
The Dashboards are available on the link: https://ivy-dynamical-dashboards.onrender.com | ||
You can filter tests by selecting choices from the various dropdowns. The link can also be saved for redirecting straight to the filtered tests in the future. The status badges are clickable, and will take you directly to the Action log of the latest workflow that ran the corresponding test. | ||
|
||
**Round Up** | ||
|
||
This should have hopefully given you a good feel for how Continuous Integration works in Ivy. | ||
|
||
If you have any questions, please feel free to reach out on `discord`_ in the `continuous integration channel`_! | ||
If you have any questions, please feel free to reach out on `discord`_ in the `continuous integration channel`_ | ||
or in the `continuous integration forum`_! | ||
|
||
**Video** | ||
|
||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why remove this?