<jupyter_start><jupyter_text>Quick Start  A Simple Example`udiff` is an automatic differentiation library with `uarray`/`unumpy`. Getting started is easy.There is a simple example:<jupyter_code># import the required packages
import uarray as ua
import unumpy as np
import numpy as onp
import udiff
from unumpy import numpy_backend

import matplotlib.pyplot as plt
with ua.set_backend(udiff.DiffArrayBackend(numpy_backend), coerce=True):
    x1 = np.array([2])
    x2 = np.array([5])
    y = np.log(x1) + x1 * x2 - np.sin(x2)
    x1_diff = y.to(x1)
    print(onp.allclose(x1_diff.value, [5.5]))<jupyter_output>True<jupyter_text>Vector-Jacobian products (VJPs)If you have used pytorch, you will find that if `y` is a tensor instead of a scalar, you will be asked to pass a  `grad_variables` in `y.backward()`. And the derivative result `x.grad` has the same shape as `x`. Where is the Jacobian matrix?The reason is that deep learning frameworks such as Tensorflow and PyTorch prohibit the derivatives with tensor by tensor, but only retain scalar by tensor. When we call `y.backward()` and enter a `grad_variables` `v`. In fact, it actually converts y into a weighted sum `l = torch.sum(y * v)`, where `l` is a scalar, and then the gradient of `x.grad` is naturally of the same shape as `x`.<jupyter_code>with ua.set_backend(udiff.DiffArrayBackend(numpy_backend), coerce=True):
    x = np.asarray(onp.linspace(-10, 10, num=1000))
    
    y = np.sin(x)
    plt.plot(onp.linspace(-10, 10, num=1000), y.value, label='y=sin(x)')
    plt.plot(onp.linspace(-10, 10, num=1000), y.to(x).value, label='dy/dx')
    plt.legend()<jupyter_output><empty_output><jupyter_text>Derivative of Higher OrderYou can get the higher derivative of `x` by calling `to` one more time.<jupyter_code>with ua.set_backend(udiff.DiffArrayBackend(numpy_backend), coerce=True):
    plt.plot(onp.linspace(-10, 10, num=1000), y.value, label='y=sin(x)')
    plt.plot(onp.linspace(-10, 10, num=1000), y.to(x).to(x).value, label=r'$d^2y/dx^2$')
    plt.legend()<jupyter_output><empty_output><jupyter_text>Jacobian MatrixIn addition to calculating `vjp` or `jvp`, we also provide an API for calculating the Jacobian matrix for users with special needs.In order to get Jacobian Matrix, we derive `y` for each element in `x`.<jupyter_code>with ua.set_backend(udiff.DiffArrayBackend(numpy_backend), coerce=True):
        x1 = np.asarray(onp.arange(2).reshape(1, 2, 1))
        x2 = np.asarray(onp.arange(2).reshape(1, 1, 2))
        y = np.matmul(x1, x2)
        print("x1's jacobian matrix: \n", y.to(x1, jacobian=True))
        print("\nx2's jacobian matrix: \n", y.to(x2, jacobian=True))<jupyter_output>x1's jacobian matrix: 
 <VJPDiffArray, id=UUID('348785de-5fa5-489d-9a3e-9f7f9f9a1440'), value=
[[[[[[0]
     [0]]]


   [[[1]
     [0]]]]



  [[[[0]
     [0]]]


   [[[0]
     [1]]]]]]
>

x2's jacobian matrix: 
 <VJPDiffArray, id=UUID('ade199a8-5270-475d-979b-e626b8b839f9'), value=
[[[[[[0 0]]]


   [[[0 0]]]]



  [[[[1 0]]]


   [[[0 1]]]]]]
>
{'fraction_non_alphanumeric': 0.14911989372301562, 'fraction_numerical': 0.043839256061109264, 'mean_word_length': 4.378571428571429, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '377798', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1241, 'n_tokens_neox': 1158, 'n_words': 307, 'path': 'docs/notebooks/quickstart.ipynb', 'repo_name': 'KanzaSheikh/udiff', 'size': '3011'}