```python
%matplotlib nbagg
from __future__ import division
from IPython.display import HTML
from IPython.display import display
from scipy.optimize import *
import scipy
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
```

## Question 1: Poiseuille's method for determining viscosity

The  volume flow rate, ${\displaystyle\frac{{\rm d}V}{{\rm d}t}}$, of fluid flowing smoothly through a horizontal tube of length $L$ and radius $r$ is given by Poiseuille's equation:
\begin{equation}
\frac{{\rm d}V}{{\rm d}t}=\frac{\pi\rho g h r^4}{8\eta L},
\end{equation}
where $\eta$ and $\rho$ are the viscosity and density, respectively, of the fluid,  $h$ is the head of pressure across the tube, and $g$ the acceleration due to gravity. 
<bf>
In an experiment the graph of the flow rate versus height has a slope measured to 7%, the length is known to 0.5%, and the radius to 8%.  
<bf>
Required:
<bf>
<blockquote>
(i) What is the fractional precision to which the viscosity is known? 
<bf>
<bf>
(ii) If more experimental time is available, should this be devoted to 
>(a) collecting more flow-rate data, 
<bf>
>(b) measuring the length,  
<bf>
>(c) the radius of the tube?

### (i) What is the fractional precision to which the viscosity is known? Express your answer as a DECIMAL. 


```python
def one_i():
    h = 0.07
    r = 0.08
    L = 0.005
    Z2 = h ** 2 + L ** 2 + r ** 2 * 16
    return np.sqrt(Z2)

print(one_i())
```

    0.32760494501762333



```python
'''TEST CELL- DO NOT DELETE'''
assert isinstance(one_i(), float) , 'Please make sure that one_i() returns a float.' 
assert one_i()>0., 'Please make sure that one_i() returns a positive value.' 

```

### (ii) If more experimental time is available, should this be devoted to 
>(a) collecting more flow-rate data, 
<bf>
>(b) measuring the length,  
<bf>
>(c) the radius of the tube?


```python
def one_ii():
    '''Your function should return a string of A,B,C'''
    # YOUR CODE HERE
    comment = "C"
    return(comment)

print(one_ii())
```

    C



```python
'''TEST CELL- DO NOT DELETE'''
assert one_ii() == 'A' or one_ii() == 'B' or one_ii() == 'C', 'Please return A or B or C in one_ii().' 
```

## Question 2: Functional error approach for Van der Waals calculation

The Van der Waals equation of state is a correction to the ideal gas law, given by the equation,

\begin{equation}
(P+\frac{a}{V_m^2})(V_m-b) = RT,
\end{equation}

where $P$ is the pressure, $V_m$ is the molar volume, $T$ is the absolute temperature, $R$ is the universal gas constant with $a$ and $b$ being species-specific Van der Waals coefficents. 

A sample of Nitrogen was measured in an experiment as,
<bf>
>Molar Volume $V_m$ = $(2.000 \pm 0.003)$x$10^{-4}m^3mol^{-1}$
<bf>
>Absolute Temperature $T$ = $298.0\pm0.2K$

and the constants are,
<blockquote>
<bf> 
$a$ = $(1.408$x$10^{-1}) m^6mol^{-2}Pa$
<bf>
$b$ = $(3.913$x$10^{-5}) m^3mol^{-1}$
<bf>
$R$ = $(8.3145) JK^{-1}mol^{-1}$
<bf>
</blockquote>
Required:
<bf>
>(i) From the given data, calculate the pressure giving your answer in MPa.
<bf>
>(ii) Calculate the uncertainty in the pressure by using the functional approach for error propagation.
<bf>
>(iii) Repeat the calculations above for 
>>$V_m = (2.000\pm0.003)\times10^{-3}\,{\rm m}^{3}\,{\rm mol}^{-1}$ and  $T=400.0 \pm 0.2K$.

### (i) From the given data, calculate the pressure giving your answer in MPa.


```python
def two_i():
    '''Your function should return the pressure in MPa'''
    R = 8.3145
    b = 3.913 * 10**(-5)
    a = 1.408 * 10**(-1)
    Vm = 2.000 * 10**(-4)
    T = 298.0
    P = R * T / (Vm - b) - (a / Vm ** 2)
    P = P / 10**6
    return round(P,2)
print(two_i())
```

    11.88



```python
'''TEST CELL- DO NOT DELETE'''
assert isinstance(two_i(), float) , 'Please make sure that two_i() returns a float.' 
```

### (ii) Calculate the uncertainty in the pressure by using the functional approach for error propagation.


```python
def two_ii():
    '''Your function should return the uncertainty'''
    # YOUR CODE HERE
    R = 8.3145
    b = 3.913 * 10**(-5)
    a = 1.408 * 10**(-1)
    Vshift = 0.003 * 10**(-4)
    Vm = 2.000 * 10**(-4)
    T = 298.0
    Tshift = 0.2
    P = (R * T / (Vm - b) - (a / Vm ** 2)) * 10 ** -6
    P_Vshift = (R * T / ((Vm + Vshift) - b) - (a / (Vm + Vshift) ** 2)) * 10 ** -6
    P_Tshift = (R * (T + Tshift) / (Vm - b) - (a / Vm ** 2)) * 10 ** -6
    sigma_T = P_Tshift - P
    sigma_V = P_Vshift - P
    ans = np.sqrt(sigma_T ** 2 + sigma_V ** 2)
    return round(ans,2)
print(two_ii())    
```

    0.02



```python
'''TEST CELL- DO NOT DELETE'''
assert isinstance(two_ii(), float) , 'Please make sure that two_ii() returns a float.' 
```

### (iii) Repeat the calculations above for 
>$V_m = (2.000\pm0.003)\times10^{-3}\,{\rm m}^{3}\,{\rm mol}^{-1}$ and  $T=400.0 \pm 0.2K$.


```python
def two_iii():
    '''Your function should return both the pressure and the uncertainty'''
    pressure_2 = 0
    uncertainty_2 = 0
    # YOUR CODE HERE
    R = 8.3145
    b = 3.913 * 10**(-5)
    a = 1.408 * 10**(-1)
    Vshift = 0.003 * 10**(-3)
    Vm = 2.000 * 10**(-3)
    T = 400.0
    Tshift = 0.2
    P = (R * T / (Vm - b) - (a / Vm ** 2)) * 10 ** -6
    P_Vshift = (R * T / ((Vm + Vshift) - b) - (a / (Vm + Vshift) ** 2)) * 10 ** -6
    P_Tshift = (R * (T + Tshift) / (Vm - b) - (a / Vm ** 2)) * 10 ** -6
    sigma_T = P_Tshift - P
    sigma_V = P_Vshift - P
    pressure_2 = round(P, 3)
    uncertainty_2 = round(np.sqrt(sigma_T ** 2 + sigma_V ** 2), 3)
    return(pressure_2,uncertainty_2)

print(two_iii())
```

    (1.661, 0.003)



```python
'''TEST CELL- DO NOT DELETE'''
test_case = two_iii()
assert isinstance(test_case[0], float) , 'Please make sure that two_iii() returns two floats.'
assert isinstance(test_case[1], float) , 'Please make sure that two_iii() returns two floats.'
```


```python
'''TEST CELL- DO NOT DELETE'''
```




    'TEST CELL- DO NOT DELETE'



## Question 3: Reverse Engineering the Incredible Goal
The separation of the posts is 7.32m, and the ball is struck from a point 22m from the near post and 29m from the far post.

Required:

> (i) Plot a graph of $\alpha_\theta$ on the y-axis vs $\alpha_L$ on the x-axis for the range of values presented in the alpha_ls array.
<bf>
>(ii) To what (common) precision must these three lengths be known to justify quoting the angle to 11 significant figures? 

*[Hint: use the functional approach using the values for the errors in the length measurements as the provided alpha__ls array]*

### (i) Plot a graph of $\alpha_\theta$ on the y-axis vs $\alpha_L$ on the x-axis for the range of values presented in the alpha_ls array.


```python
def three_i():
    a = 22
    b = 29
    c = 7.32
    import math
    alpha_ls = [0.1, 0.01, 0.001, 0.0001, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09, 1e-10, 1e-11, 1e-12, 7.65157214e-13]
    alpha_thetas = []
    angel = degree(a, b, c)
    for i in alpha_ls:
        degree_errorA = degree(a + i, b, c) - angel
        degree_errorB = degree(a, b + i, c) - angel
        degree_errorC = degree(a, b, c + i) - angel
        alpha_theta = np.sqrt(degree_errorA ** 2 + degree_errorB ** 2 + degree_errorC ** 2)
        alpha_thetas.append(alpha_theta)
    plt.figure()
    plt.plot(-np.log10(alpha_ls) - 1, alpha_thetas)
    plt.show()
    '''Your function should plot the graph and return the alpha_thetas array.'''
    # YOUR CODE HERE
    return(alpha_thetas)

def degree(a, b, c):
    angel = 180 / np.pi * np.arccos((a**2 + b**2 - c**2) / (2 * a * b))
    return angel

three_i()

# def func_solve(i):
#     a = 22
#     b = 29
#     c = 7.32
#     angel = degree(a, b, c)
#     print(i)
#     degree_errorA = degree(a + i, b, c) - angel
#     degree_errorB = degree(a, b + i, c) - angel
#     degree_errorC = degree(a, b, c + i) - angel
#     alpha_theta = np.sqrt(degree_errorA ** 2 + degree_errorB ** 2 + degree_errorC ** 2)
#     return(alpha_theta - 1e-11)

# ans = fsolve(func_solve, 1e-10)
# print(ans)


1e-11
```


    <IPython.core.display.Javascript object>









    [1.2897314602156371,
     0.13011222242803275,
     0.013040258626603154,
     0.001304333860986745,
     0.0001304364837778715,
     1.3043679352599412e-05,
     1.304368267610034e-06,
     1.3043677404434444e-07,
     1.3043593635272353e-08,
     1.3043345643680718e-09,
     1.3050678994651162e-10,
     1.3015082449976125e-11,
     9.936340851320653e-12]




```python
'''TEST CELL- DO NOT DELETE'''
```




    'TEST CELL- DO NOT DELETE'



### (ii) To what (common) precision must these three lengths be known to justify quoting the angle to 11 significant figures?

Using fsolve in scipy, the outcome that quoting the angle to 11 significat figure need precision 7.65157214e-13, so at least, the common precision should be 1e-13.

## Question 4: Linear Regression/Weighted Fit

The data  plotted in Fig 6.1(d) relating to the degradation of the signal to noise ratio from a frequency to voltage converter near harmonics of the mains frequency are listed below.
\begin{equation}
\begin{array}{lcccccc}
\hline
{\rm frequency~(Hz)} &10&20&30&40&50&60\\
{\rm voltage~(mV)} &16&45&64&75&70&115\\
{\rm error~(mV)}   &5&5&5&5&30&5\\
\hline
{\rm frequency~(Hz)} &70&80&90&100&110&\\
{\rm voltage~(mV)} &142&167&183&160&221&\\
{\rm error~(mV)}   &5&5&5&30&5&\\
\hline
\end{array} 
\end{equation}

This data is also contained in the file 'linear_regression.csv'.

Required: 
<bf>
> (i) Calculate the best-fit gradient and intercept and associated errors using a weighted fit. You may use the curve_fit function.


```python
data = pd.read_csv('linear_regression.csv')
frequencies = data.iloc[:,0]
voltages = data.iloc[:,1]
voltage_errors = data.iloc[:,2]

def f(frequencies, a,b):
    return a*frequencies+b

def best_fit_params():
    '''Your function should return the gradient,gradient_error,intercept,intercept_error'''
    # YOUR CODE HERE
    gradient = 0
    gradient_error = 0
    intercept = 0
    intercept_error = 0
    plt.figure()
    popt, pcov = curve_fit(f, frequencies, voltages, sigma = voltage_errors)
    plt.plot(frequencies, voltages, 'b-', label='origin_data')
    plt.plot(frequencies, f(frequencies, *popt), 'r-', label='fit: a=%5.3f, b=%5.3f' % tuple(popt))
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()
    plt.show()
    total_sub = 0
    delta = 0
    total_x = 0
    total_x2 = 0
    total_y = 0
    total_xy = 0
    gradient = popt[0]
    intercept = popt[1]
    for i in range(len(frequencies)):
        total_sub += (voltages[i] - gradient * frequencies[i] - intercept) ** 2
        total_x += frequencies[i]
        total_x2 += frequencies[i] ** 2
    delta = len(frequencies) * total_x2 - total_x ** 2
    common_uncertainty = np.sqrt((1 / (len(frequencies) - 2)) * total_sub)
#     gradient_error = common_uncertainty * np.sqrt(len(frequencies) / delta)
#     intercept_error = common_uncertainty * np.sqrt(total_x2 / delta)
    gradient_error = pcov[0,0]**0.5
    intercept_error = pcov[1,1]**0.5
    return(gradient,gradient_error,intercept,intercept_error)

best_fit_params()
```


    <IPython.core.display.Javascript object>









    (2.0284648194758716,
     0.05231193382948591,
     -0.9474963040606827,
     3.4085972274383725)




```python
'''TEST CELL- DO NOT DELETE'''
assert isinstance(best_fit_params(), (list, tuple, np.ndarray)), \
    'Please make sure that best_fit_params() returns a list/array of floats.'

```


    <IPython.core.display.Javascript object>







```python
'''TEST CELL- DO NOT DELETE'''
```




    'TEST CELL- DO NOT DELETE'




```python
'''TEST CELL- DO NOT DELETE'''
```




    'TEST CELL- DO NOT DELETE'




```python
'''TEST CELL- DO NOT DELETE'''
```




    'TEST CELL- DO NOT DELETE'



## Question 5: Error bars from a $\chi^2$ minimisation
|--|Unweighted | Weighted |
| --- | --- | --- |
| Gradient | (1.9$\pm$0.2)mV/Hz | (2.03$\pm$0.05)mV/Hz |
| Intercept | (0$\pm$1)x10mV | (-1$\pm$3)mV |

Required:
<bf>
>(i) For the data set of the previous question, write your own code to perform a $\chi^2$ minimisation. You may use the mininmize function, but not the curve_fit function.
<bf>
>(ii) Verify that $\chi^{2}_{\rm{min}}$ is obtained for the same values of the parameters as are listed in the table above. 
<bf>
>(iii) By following the procedure of $\chi^2\rightarrow\chi^2_{\rm min}+1$ outlined in Section 6.5 in Hughes and Hase and the figure above, check that the error bars for $m$ and $c$ are in agreement with the table above.  Include explicitly the first 5 steps of the procedure shown in the figure above for the calculation of the slope.


### (i) For the data set of the previous question, write code to perform a $\chi^2$ minimisation.


```python
def func_solve(t):
    kai2 = 0
    for i in range(len(frequencies)):
        kai2 += (voltages[i] - frequencies[i] * t[0] - t[1]) ** 2 / (voltage_errors[i] ** 2)
    return kai2
m_init = 1
c_init = 1
index = [m_init, c_init]
res = minimize(func_solve, index)
print(res.fun)
```

    9.115903588053289


### (ii) Verify that $\chi^{2}_{\rm{min}}$ is obtained for the same values of the parameters as are listed in the table above. 


```python
data = pd.read_csv('linear_regression.csv')

def five_ii(): 
    '''Your function must return the gradient and intercept'''
    gradient = 2.03
    intercept = -1
    kai2 = 0
    for i in range(len(frequencies)):
        kai2 += (voltages[i] - frequencies[i] * gradient - intercept) ** 2 / (voltage_errors[i] ** 2)
    print(kai2)
    # YOUR CODE HERE
    return(gradient,intercept)

five_ii()
```

    9.11721111111111





    (2.03, -1)




```python
'''TEST CELL- DO NOT DELETE'''
assert isinstance(five_ii(), (list, tuple, np.ndarray)), \
    'Please make sure that five_ii() returns a list/array of two floats.'
```

    9.11721111111111


### (iii) By following the procedure of $\chi^2\rightarrow\chi^2_{\rm min}+1$ outlined in Section 6.5 in Hughes and Hase and the figure above, check that the error bars for $m$ and $c$ are in agreement with the table above.  Include explicitly the first 5 steps of the procedure shown in the figure above for the calculation of the slope.


```python
def func_solvem(m, arg):
    kai2 = 0
    for i in range(len(frequencies)):
        kai2 += (voltages[i] - frequencies[i] * m - arg[0]) ** 2 / (voltage_errors[i] ** 2)
    return kai2 - (arg[1] + 1)

def func_solvec(c, arg):
    kai2 = 0
    for i in range(len(frequencies)):
        kai2 += (voltages[i] - frequencies[i] * arg[0] - c) ** 2 / (voltage_errors[i] ** 2)
    return kai2 - (arg[1] + 1)

def func_solvem2(c, arg):
    kai2 = 0
    for i in range(len(frequencies)):
        kai2 += (voltages[i] - frequencies[i] * arg[0] - c) ** 2 / (voltage_errors[i] ** 2)
    return kai2

def func_solvec2(m, arg):
    kai2 = 0
    for i in range(len(frequencies)):
        kai2 += (voltages[i] - frequencies[i] * m - arg[0]) ** 2 / (voltage_errors[i] ** 2)
    return kai2

def five_iii():
    error_m = 0
    error_c = 0
    chi_now = res.fun
    m = res.x[0]
    c = res.x[1]
    print(m)
    for i in range(10):
        m_fixed = fsolve(func_solvem, m, [c, chi_now])
        res_now = minimize(func_solvem2, c, [m_fixed])
        m = m_fixed
        c = res_now.x[0]
    error_m = abs(res.x[0] - m)
    
    m = res.x[0]
    c = res.x[1]
    chi_now = res.fun
    for i in range(10):
        c_fixed = fsolve(func_solvec, c, [m, chi_now])
        res_now = minimize(func_solvec2, m, [c_fixed])
        c = c_fixed
        m = res_now.x[0]
        
    error_c = abs(res.x[1] - c)
    # YOUR CODE HERE
    return(error_m,error_c)

five_iii()
```

    2.0284647949240395
    [2.02851608]
    [2.05400358]
    [2.0709015]
    [2.07860821]
    [2.08035088]
    [2.08044287]
    [2.08044313]
    [2.08044313]
    [2.08044313]
    [2.08044313]
    [-0.97665438]
    [-2.63113595]
    [-3.72349197]
    [-4.21832112]
    [-4.32867791]
    [-4.3343402]
    [-4.33435514]
    [-4.33435514]
    [-4.33435514]
    [-4.33435514]


    /opt/conda/lib/python3.6/site-packages/scipy/optimize/minpack.py:163: RuntimeWarning: The iteration is not making good progress, as measured by the 
      improvement from the last ten iterations.
      warnings.warn(msg, RuntimeWarning)





    (array([0.05197833]), array([3.38686011]))




```python
'''TEST CELL- DO NOT DELETE'''
assert isinstance(five_iii(), (list, tuple, np.ndarray)), \
    'Please make sure that five_ii() returns a list/array of two floats.'
```

## Question 6- Strategies for error bars

\begin{equation}
\begin{array}{lccccc}
\hline
x &1&2&3&4&5\\
y &51&103&150&199&251\\
\alpha_{y}   &1&1&2&2&3\\
\hline
x &6&7&8&9&10\\
y &303&347&398&452&512\\
\alpha_{y}   &3&4&5&6&7\\
\hline
\end{array} 
\end{equation}

Required:
<bf>
>(i) Calculate the weighted best-fit values of the slope, intercept, and their uncertainties.
<bf>
>(ii) If the data set had been homoscedastic, with all the errors equal, $\alpha_{y}=4$, calculate the weighted best-fit values of the slope, intercept, and their uncertainties.
<bf>
>(iii) If the experimenter took greater time to collect the first and last data points, for which $\alpha_{y}=1$, at the expense of all of the other data points, for which $\alpha_{y}=8$, calculate the weighted best-fit values of the slope, intercept, and their uncertainties.
<bf>
>(iv) Comment on your results.
<bf>
>(v) Plot the original data from the table including error bars. On the same plot, show the fitted function calculated in (i).

### (i) Calculate the weighted best-fit values of the slope, intercept, and their uncertainties.


```python
xs = [1,2,3,4,5,6,7,8,9,10]
ys = [51,103,150,199,251,303,347,398,452,512]
errors = [1,1,2,2,3,3,4,5,6,7]

def f(x,a,b):
    return(a*x+b)

def six_i():
    errors = [1,1,2,2,3,3,4,5,6,7]
    slope = 0
    intercept = 0
    slope_uncertainty = 0
    intercept_uncertainty = 0
    popt, pcov = curve_fit(f, xs, ys, sigma = errors)
    slope = popt[0]
    intercept = popt[1]
    slope_uncertainty = pcov[0,0]**0.5
    intercept_uncertainty = pcov[1,1]**0.5
    # YOUR CODE HERE
    return(slope,intercept,slope_uncertainty,intercept_uncertainty)

six_i()
```




    (49.89790138742879, 1.726770709105078, 0.332201663924493, 1.0360068862703642)




```python
'''TEST CELL- DO NOT DELETE'''
assert isinstance(six_i(), (list, tuple, np.ndarray)), \
    'Please make sure that six_i() returns a list/array of four floats.'
```

### (ii) If the data set had been homoscedastic, with all the errors equal, $\alpha_{y}=4$, calculate the weighted best-fit values of the slope, intercept, and their uncertainties.


```python
def six_ii():
    errors = [4,4,4,4,4,4,4,4,4,4]
    slope = 0
    intercept = 0
    slope_uncertainty = 0
    intercept_uncertainty = 0
    # YOUR CODE HERE
    popt, pcov = curve_fit(f, xs, ys, sigma = errors)
    slope = popt[0]
    intercept = popt[1]
    slope_uncertainty = pcov[0,0]**0.5
    intercept_uncertainty = pcov[1,1]**0.5
    return(slope,intercept,slope_uncertainty,intercept_uncertainty)

six_ii()
```




    (50.47272726877133,
     -0.9999999782423065,
     0.45720434535997834,
     2.836878357799031)




```python
'''TEST CELL- DO NOT DELETE'''
assert isinstance(six_ii(), (list, tuple, np.ndarray)), \
    'Please make sure that six_ii() returns a list/array of four floats.'
```

### (iii) If the experimenter took greater time to collect the first and last data points, for which $\alpha_{y}=1$, at the expense of all of the other data points, for which $\alpha_{y}=8$, calculate the weighted best-fit values of the slope, intercept, and their uncertainties.


```python
def six_iii():
    errors = [1,8,8,8,8,8,8,8,8,1]
    slope = 0
    intercept = 0
    slope_uncertainty = 0
    intercept_uncertainty = 0
    # YOUR CODE HERE
    popt, pcov = curve_fit(f, xs, ys, sigma = errors)
    slope = popt[0]
    intercept = popt[1]
    slope_uncertainty = pcov[0,0]**0.5
    intercept_uncertainty = pcov[1,1]**0.5
    return(slope,intercept,slope_uncertainty,intercept_uncertainty)

six_iii()
```




    (51.198747152729084,
     -0.45340345705795704,
     0.13868714281431524,
     0.9769093572145309)




```python
'''TEST CELL- DO NOT DELETE'''
assert isinstance(six_iii(), (list, tuple, np.ndarray)), \
    'Please make sure that six_iii() returns a list/array of four floats.'
```

### (iv) Comment on your results.

If the first point and the last point have very small error, the straight line is roughly fixed, so the intercept uncertainty and slope uncertainty are very small. Though the straight line may not fit with the true data, maybe with a lot of errors, but the uncertainty are very small. In my opinion, if two points, no matter whether they are the first two points or last two points, have no error, the line will be fixed without any uncertainty.

### (v) Plot the original data from the table including error bars. On the same plot, show the fitted function calculated in (i).


```python
plt.figure()
slope, intercept,_,_ = six_i()
ys_pred = []
for x in xs:
    ys_pred.append(slope * x + intercept)
plt.plot(xs, ys_pred, 'r-', label='fit: a=%5.3f, b=%5.3f' % tuple(popt))
plt.errorbar(xs, ys, yerr=errors, label='point with error bar', ls = '')# YOUR CODE HERE
plt.legend()
plt.show()
```


    <IPython.core.display.Javascript object>







```python

```

85357ea61d911e08561f63a5d617ea79d556aebf
ipynb
Jupyter Notebook
Core 1/data analysis/ipython notebook/Homework 2/Assignment 2.ipynb
hwhv66/CourseworkDurham
eec812d38776cf61c6fd7925e5706c8698926fa3
MIT
Core 1/data analysis/ipython notebook/Homework 2/Assignment 2.ipynb
hwhv66/CourseworkDurham
eec812d38776cf61c6fd7925e5706c8698926fa3
MIT
Core 1/data analysis/ipython notebook/Homework 2/Assignment 2.ipynb
hwhv66/CourseworkDurham
eec812d38776cf61c6fd7925e5706c8698926fa3
MIT
2021-11-24T06:35:40.000Z
2021-11-24T06:35:40.000Z
Qwen/Qwen-72B
1. YES
2. YES