Skip to content
Snippets Groups Projects
Commit a4bb2e74 authored by W. Spencer Smith's avatar W. Spencer Smith
Browse files

A2 Solution

parent 7979ce3d
No related branches found
No related tags found
No related merge requests found
Showing
with 3795 additions and 0 deletions
PY = pytest
PYFLAGS = --cov
DOXY = doxygen
DOXYCFG = doxConfig
RMDIR = rm -rf
.PHONY: test doc clean
test:
$(PY) $(PYFLAGS) src
doc:
$(DOXY) $(DOXYCFG)
cd latex && $(MAKE)
clean:
@- $(RMDIR) html
@- $(RMDIR) latex
This diff is collapsed.
File added
\documentclass[12pt]{article}
\usepackage{graphicx}
\usepackage{paralist}
\usepackage{listings}
\usepackage{booktabs}
\oddsidemargin 0mm
\evensidemargin 0mm
\textwidth 160mm
\textheight 200mm
\pagestyle {plain}
\pagenumbering{arabic}
\newcounter{stepnum}
\title{Assignment 2 Solution}
\author{Henry M. 000000000}
\date{\today}
\begin {document}
\maketitle
Introductory blurb.
\section{Testing of the Original Program}
Description of approach to testing. Rationale for test case selection. Summary
of results. Any problems uncovered through testing.
\section{Results of Testing Partner's Code}
Summary of results.
\section{Discussion of Test Results}
\subsection{Problems with Original Code}
\subsection{Problems with Partner's Code}
\subsection{Problems with Assignment Specification}
\section{Answers}
\begin{enumerate}
\item What is the mathematical specification of the \texttt{SeqServices} access
program isInBounds(X, x) if the assumption that X is ascending is removed?
output: $:= \exists\,( i, j \,|\, i, j \in [0..|X| - 1] : X_i \leq x \leq X_j )$
\\exception: none
\item How would you modify \texttt{CurveADT.py} to support cubic interpolation?
\begin{enumerate}
\item modify MAX\_ORDER constant in curveADT from 2 to 3
\item Add an access program, interpCubic that calls scipy with interp1d with kind=3
\end{enumerate}
\item What is your critique of the CurveADT module's interface. In particular,
comment on whether the exported access programs provide an interface that is
consistent, essential, general, minimal and opaque.
- exceptions should be in SeqServices
\item What is your critique of the Data abstract object's interface. In
particular, comment on whether the exported access programs provide an
interface that is consistent, essential, general, minimal and opaque.
- no size method
\end{enumerate}
\newpage
\lstset{language=Python, basicstyle=\tiny, breaklines=true, showspaces=false,
showstringspaces=false, breakatwhitespace=true}
%\lstset{language=C,linewidth=.94\textwidth,xleftmargin=1.1cm}
\def\thesection{\Alph{section}}
\section{Code for CurveADT.py}
\noindent \lstinputlisting{../src/CurveADT.py}
\newpage
\section{Code for Data.py}
\noindent \lstinputlisting{../src/Data.py}
\newpage
\section{Code for SeqServices.py}
\noindent \lstinputlisting{../src/SeqServices.py}
\newpage
\section{Code for Plot.py}
\noindent \lstinputlisting{../src/Plot.py}
\newpage
\section{Code for Load.py}
\noindent \lstinputlisting{../src/Load.py}
\newpage
\section{Code for Partner's CurveADT.py}
\noindent \lstinputlisting{../partner/CurveADT.py}
\newpage
\section{Code for Partner's Data.py}
\noindent \lstinputlisting{../partner/Data.py}
\newpage
\section{Code for Partner's SeqServices.py}
\noindent \lstinputlisting{../partner/SeqServices.py}
\newpage
\section{Makefile}
\lstset{language=make}
\noindent \lstinputlisting{../Makefile}
\end {document}
#from CurveADT import *
from CurveADTscipy import *
from Data import *
from Load import *
from Plot import *
X = range(11)
Y = list(map(lambda x: x**2, X))
c1 = CurveT(X, Y, 1)
print(c1.eval(6.5))
PlotCurve(c1, 50)
c2 = CurveT(X, Y, 2)
print(c2.eval(6.5))
PlotCurve(c2, 5)
#PlotCurve(c2, 50) will look strange because of edge effects
#scipy and lambda version for A2 have same behaviour
Load('glass.csv')
c3 = Data.getC(9)
PlotCurve(c3, 50)
print(Data.eval(18, 34))
c4 = Data.slice(18, 1)
PlotCurve(c4, 50)
print(c4.eval(34))
## @file CurveT.py
# @author Steven Palmer and Henry Madej
# @brief CurveT
# @date 20/02/2018
from Exceptions import *
from SeqServices import *
## @brief An abstract data type that represents a Curve
class CurveT:
MAX_ORDER = 2
DX = 1E-3
## @brief CurveT constructor
# @details takes two lists of of x's and y's where the values are sorted in
# ascending order, and order i of the function representing the curve
# @param X list of x values sorted in ascending order
# @param Y list of corresponding y values to the the x's in the X list
# @param i order of the function represented by the data points (x,y) pairs
def __init__(self, X, Y, i):
if not isAscending(X):
raise IndepVarNotAscending
if i < 1 or i > CurveT.MAX_ORDER:
raise InvalidInterpOrder
if len(X) != len(Y):
raise SeqSizeMismatch
self.__minx = X[0]
self.__maxx = X[-1]
self.__o = i
if self.__o == 1:
self.__f = lambda v: interpLin(X[index(X, v)], Y[index(X, v)], X[index(X, v) + 1], Y[index(X, v) + 1], v)
elif self.__o == 2:
self.__f = lambda v: interpQuad(X[index(X, v) - 1], Y[index(X, v) - 1], X[index(X, v)], Y[index(X, v)], X[index(X, v) + 1], Y[index(X, v) + 1], v)
## @brief minD returns the minimal x value
# @return value representing the minimal x value
def minD(self):
return self.__minx
## @brief maxD returns the maximal x value
# @return value representing the maximal x value
def maxD(self):
return self.__maxx
## @brief order returns the order of the function represented by the curve
# @return the value of the order of the curve
def order(self):
return self.__o
## @brief eval evaluates the function at a given x value
# @details if x is within the range of x values for the curve it will return
# a y value for the curve
# @param x value of x to evaluate the curve at
# @exception OutOfDomain - if x value is not within the range of the curve's
# x values
# @return the value of the curve at x
def eval(self, x):
if x < self.__minx or x > self.__maxx:
raise OutOfDomain
return self.__f(x)
## @brief approximates the first derivate of the curve
# @details uses forward divided difference
# @param x value of x for the first derivate to be evaluated at
# @return the value of the first derivate at evaluated x
def dfdx(self, x):
if x < self.__minx or x > self.__maxx:
raise OutOfDomain
return self.__f(x + self.DX) - self.__f(x) / self.DX
## @brief approximates the second derivate of the curve
# @details uses forward divided difference
# @param x value of x for the second derivate to be evaluated at
# @return the value of the second derivate at evaluated x
def d2fdx2(self, x):
if x < self.__minx or x > self.__maxx:
raise OutOfDomain
return self.__f(x + 2*self.DX) - 2*self.__f(x + self.DX) + self.__f(x) / self.DX**2
\ No newline at end of file
## @file CurveT.py
# @author Steven Palmer and Henry Madej
# @brief CurveT
# @date 20/02/2018
from Exceptions import *
from SeqServices import *
from scipy.interpolate import interp1d
## @brief An abstract data type that represents a Curve
class CurveT:
MAX_ORDER = 2
## @brief
# @details
def __init__(self, X, Y, i):
if not isAscending(X):
raise IndepVarNotAscending
if i < 1 or i > CurveT.MAX_ORDER:
raise InvalidInterpOrder
self.__minx = X[0]
self.__maxx = X[-1]
self.__o = i
if self.__o == 1:
self.__f = interp1d(X, Y, 'linear')
elif self.__o == 2:
self.__f = interp1d(X, Y, 'quadratic')
## @brief
# @details
def minD(self):
return self.__minx
## @brief
# @details
def maxD(self):
return self.__maxx
## @brief
# @details
def order(self):
return self.__o
## @brief
# @details
def eval(self, x):
if x < self.__minx or x > self.__maxx:
raise OutOfDomain
return float(self.__f(x))
## @brief
# @details
def dfdx(self, x):
if x < self.__minx or x > self.__maxx:
raise OutOfDomain
return self.__f(x + DX) - self.__f(x) / DX
## @brief
# @details
def d2fdx2(self, x):
if x < self.__minx or x > self.__maxx:
raise OutOfDomain
return self.__f(x + 2*DX) - 2*self.__f(x + DX) + self.__f(x) / DX**2
\ No newline at end of file
from Exceptions import *
from SeqServices import *
class CurveT:
MAX_ORDER = 2
DX = 1E-3
def __init__(self, X, Y, i):
if not isAscending(X):
raise IndepVarNotAscending
if i < 1 or i > CurveT.MAX_ORDER:
raise InvalidInterpOrder
self.__minx = X[0]
self.__maxx = X[-1]
self.__o = i
self.X = X
self.Y = Y
def minD(self):
return self.__minx
def maxD(self):
return self.__maxx
def order(self):
return self.__o
def eval(self, x):
if x < self.__minx or x > self.__maxx:
raise OutOfDomain
if self.__o == 1:
return (lambda v: interpLin(self.X[index(self.X, v)], self.Y[index(self.X, v)], self.X[index(self.X, v) + 1], self.Y[index(self.X, v) + 1], v))(x)
elif self.__o == 2:
return (lambda v: interpQuad(self.X[index(self.X, v) - 1], self.Y[index(self.X, v) - 1], self.X[index(X, v)], self.Y[index(self.X, v)], self.X[index(self.X, v) + 1], self.Y[index(self.X, v) + 1], v))(x)
def dfdx(self, x):
if x < self.__minx or x > self.__maxx:
raise OutOfDomain
return self.__f(x + DX) - self.__f(x) / DX
def d2fdx2(self, x):
if x < self.__minx or x > self.__maxx:
raise OutOfDomain
return self.__f(x + 2*DX) - 2*self.__f(x + DX) + self.__f(x) / DX**2
## @file Data.py
# @author Steven Palmer and Henry Madej
# @brief Data
# @date 20/02/2018
from CurveADT import *
import SeqServices
from Exceptions import *
## @brief An abstract data type for storing data
class Data:
MAX_SIZE = 10
S = []
Z = []
## @brief init initial data structure
@staticmethod
def init():
Data.S = []
Data.Z = []
## @brief add adds a pair to the data structure
# @exception throws Full exception is structure is full
# @exception throws IndepVarNotAscending if z is less than or equal the last
# value in the structure
# @param s dependant variable value added to the structure
# @param z independent variable value aded to the structure
@staticmethod
def add(s, z):
if len(Data.S) == Data.MAX_SIZE:
raise Full
if Data.Z:
if z <= Data.Z[-1]:
raise IndepVarNotAscending
Data.S.append(s)
Data.Z.append(z)
## @brief getC gets value of the dependant variable at index i
# @param i index of dependant variable value
# @return value of dependant variable at index i
@staticmethod
def getC(i):
if not (0 <= i < len(Data.S)):
raise InvalidIndex
return Data.S[i]
## @brief eval evaluates the a curve at a particular value of x
# @details Uses linear interplation to find the value of the curve at x
# @param x the value to evaluate the curve at
# @param z value adjacent to the x value
# @return value of the curve at the specified value of x
@staticmethod
def eval(x, z):
if not isInBounds(Data.Z, z):
raise OutOfDomain
j = SeqServices.index(Data.Z, z)
return interpLin(Data.Z[j], Data.S[j].eval(x), Data.Z[j+1], Data.S[j+1].eval(x), z)
## @brief slice slices a curve returning a subset of the original curve
# @param x the x to slice at
# @param i the order of the curve that is being sliced
# @return CurveT
@staticmethod
def slice(x, i):
Y = [s.eval(x) for s in Data.S]
return CurveT(Data.Z, Y, i)
## @brief exception when independent variable is not ascending
class IndepVarNotAscending(Exception):
pass
## @brief exception when the order of interplation is incorrect
class InvalidInterpOrder(Exception):
pass
## @brief exception when given value is not within bounds
class OutOfDomain(Exception):
pass
## @brief exception when data structure is full
class Full(Exception):
pass
## @brief exception when two sequences are of different size
class SeqSizeMismatch(Exception):
pass
## @brief exception when given index is not in bounds
class InvalidIndex(Exception):
pass
## @file Load.py
# @author Steven Palmer and Henry Madej
# @brief function for retrieving data from file
# @date 20/02/2018
from Data import *
from CurveADT import *
## @brief Load reads in data from a file, storing it in Data
# @param s the name of the file to be read
def Load(s):
with open(s, 'r') as infile:
contents = infile.readlines()
contents = [x.strip().split(',') for x in contents]
zs = contents[0]
os = contents[1]
pts = contents[2:]
Data.init()
for i in range(len(zs)):
X = []
Y = []
for j in range(0, len(pts)):
if pts[j][i*2] == '':
break
X.append(float(pts[j][i*2]))
Y.append(float(pts[j][i*2+1]))
Data.add(CurveT(X,Y,int(os[i])), float(zs[i]))
## @file Plot.py
# @author Steven Palmer and Henry Madej
# @brief Plotting functions
# @date 20/02/2018
from matplotlib.pyplot import *
from Exceptions import *
from CurveADT import *
## @brief PlotSeq plots a sequence of (x, y) pairs
# @param X corresponding x values of the sequence
# @param Y corresponding y values of the sequence
def PlotSeq(X, Y):
if len(X) != len(Y):
raise SeqSizeMismatch
figure(dpi=100)
plot(X, Y)
show()
## @brief PlotCurve plots a curve
# @param c the curve to be plotted
# @param n number of equally spaced points
def PlotCurve(c, n):
d = (c.maxD() - c.minD())/(n+1)
curX = c.minD()
X = []
Y = []
for i in range(n):
curX = curX + d
X.append(curX)
Y.append(c.eval(curX))
PlotSeq(X, Y)
## @file SeqServices.py
# @author Steven Palmer and Henry Madej
# @brief Provides the services for working with sequences
# @date 20/02/2018
## @brief isAscending checks if a sequence is in ascending order
# @param X sequence to be checked for ascending order
# @return boolean False if not in ascending order True otherwise
def isAscending(X):
for i in range(0, len(X) - 1):
if X[i + 1] < X[i]:
return False
return True
## @brief isInBounds checks if value (x) is within bounds of sequence (X)
# @param X sequence to be checked against
# @param x value to be checked for in bounds
# @return boolean False if not in bounds True otherwise
def isInBounds(X, x):
return X[0] <= x and x <= X[len(X) - 1]
## @brief interpLin given two points interpolates the y value for a given x
# @details linear interpolation
# @param x1 x value of the first point
# @param y1 y value of the first point
# @param x2 x value of the second point
# @param y2 y value of the second point
# @param x value for which we want to interpolate a y value
# @return y value for given x value
def interpLin(x1, y1, x2, y2, x):
return (y2 - y1) / (x2 - x1) * (x - x1) + y1
## @brief interpQuad given three points interpolates the y value for given x
# @details quadratic interpolation
# @param x0 x value of the first point
# @param y0 y value of the first point
# @param x1 x value of the second point
# @param y1 y value of the second point
# @param x2 x value of the third point
# @param y2 y value of the third point
# @param x value for which we want to interpolate a y value
# @return y value for given x value
def interpQuad(x0, y0, x1, y1, x2, y2, x):
return y1 + (y2 - y0) / (x2 - x0) * (x - x1) + (y2 - 2*y1 + y0) / (2*(x2 - x1)**2) * (x - x1)**2
## @brief index given a sequence and value, returns index of that value if in
# sequence
# @details Seq[i] <= x <= Seq[i+1]
# @param X sequence to be searched
# @param x value to look for
# @return index of closest value to given x in sequence
def index(X, x):
for i in range(0, len(X) - 1):
if X[i] <= x and x < X[i+1]:
return i
This diff is collapsed.
1,2,3
1,2,2
1,1,1,5,1,10
2,2,1.5,7,2,40
3,3,2,20,3,100
,,2.5,40,,
,,3,78,,
\ No newline at end of file
from CurveADT import *
import Data
import Load
import Plot
import Exceptions
import SeqServices
from pytest import *
class TestCurveADT:
def test_minD(self):
curve = CurveT([0, 50, 99], [1, 2, 3], 1)
assert curve.minD() == 0
def test_maxD(self):
curve = CurveT([0, 50, 99], [1, 2, 3], 1)
assert curve.maxD() == 99
def test_order(self):
curve = CurveT([0, 50, 99], [1, 2, 3], 1)
assert curve.order() == 1
def test_raises_indepVarNotAscending(self):
with raises(Exceptions.IndepVarNotAscending):
CurveT([ 0, -1 ], [ 0, 0 ], 1)
def test_raises_SeqSizeMismatch(self):
with raises(Exceptions.SeqSizeMismatch):
CurveT([ 0 ], [ 0, 0 ], 1)
def test_raises_OutOfDomain_eval_below(self):
curve = CurveT([ 0, 1, 2 ], [ 0, 0, 0 ], 1)
with raises(Exceptions.OutOfDomain):
curve.eval(-1)
def test_raises_OutOfDomain_eval_above(self):
curve = CurveT([ 0, 1, 2 ], [ 0, 0, 0 ], 1)
with raises(Exceptions.OutOfDomain):
curve.eval(3)
def test_raises_OutOfDomain_dfdx_below(self):
curve = CurveT([ 0, 1, 2 ], [ 0, 0, 0 ], 1)
with raises(Exceptions.OutOfDomain):
curve.dfdx(-1)
def test_raises_OutOfDomain_dfdx_above(self):
curve = CurveT([ 0, 1, 2 ], [ 0, 0, 0 ], 1)
with raises(Exceptions.OutOfDomain):
curve.dfdx(3)
def test_raises_OutOfDomain_d2fdx2_below(self):
curve = CurveT([ 0, 1, 2 ], [ 0, 0, 0 ], 1)
with raises(Exceptions.OutOfDomain):
curve.d2fdx2(-1)
def test_raises_OutOfDomain_d2fdx2_above(self):
curve = CurveT([ 0, 1, 2 ], [ 0, 0, 0 ], 1)
with raises(Exceptions.OutOfDomain):
curve.d2fdx2(3)
def test_eval_linear(self):
curve = CurveT([0, 1, 3], [0, 1, 3], 1)
assert curve.eval(2) == approx(2, 0.1)
def test_eval_quadratic(self):
curve = CurveT([0, 1, 2], [1, 0, 1], 2)
assert curve.eval(0.5) == approx(1.25, 0.1)
def test_dfdx(self):
curve = CurveT([0, 1], [0, 1000], 1)
assert curve.dfdx(0) == approx(1, 0.0001)
def test_dfdx_2(self):
curve = CurveT([0, 1], [0, 1], 1)
assert curve.dfdx(0) == approx(0.001, 0.0001)
def test_d2fdx2(self):
curve = CurveT([0, 1], [0, 1], 1)
assert curve.d2fdx2(0) == approx(0, 0.0001)
def test_d2fdx2_2(self):
curve = CurveT([0, 1], [0, 1000], 1)
assert curve.d2fdx2(0) == approx(0, 0.0001)
class TestData:
def test_constructor_S(self):
Data.Data.init()
assert Data.Data.S == []
def test_constructor_Z(self):
Data.Data.init()
assert Data.Data.Z == []
def test_constructor_MAX(self):
Data.Data.init()
assert Data.Data.MAX_SIZE == 10
def test_add(self):
Data.Data.init()
test_curve_a = CurveT([0,1,2], [0,1,2], 1)
test_curve_b = CurveT([0,1,2], [0,1,2], 1)
Data.Data.add(test_curve_a, 0)
Data.Data.add(test_curve_b, 1)
assert Data.Data.S[0] == test_curve_a
assert Data.Data.S[1] == test_curve_b
def test_add_full_exception(self):
Data.Data.init()
test_curve_a = CurveT([0,1,2], [0,1,2], 1)
with raises(Full):
for x in range(11):
Data.Data.add(test_curve_a, x)
def test_getC(self):
Data.Data.init()
test_curve_a = CurveT([0,1,2], [0,1,2], 1)
test_curve_b = CurveT([0,1,2], [0,1,2], 1)
Data.Data.add(test_curve_a, 0)
Data.Data.add(test_curve_b, 1)
assert Data.Data.getC(0) == test_curve_a
assert Data.Data.getC(1) == test_curve_b
def test_getC_empty(self):
Data.Data.init()
with raises(InvalidIndex):
Data.Data.getC(0)
def test_getC_invalid_index_below(self):
Data.Data.init()
test_curve_a = CurveT([0,1,2], [0,1,2], 1)
test_curve_b = CurveT([0,1,2], [0,1,2], 1)
Data.Data.add(test_curve_a, 0)
Data.Data.add(test_curve_b, 1)
with raises(InvalidIndex):
Data.Data.getC(-1)
def test_getC_invalid_index_above(self):
Data.Data.init()
test_curve_a = CurveT([0,1,2], [0,1,2], 1)
test_curve_b = CurveT([0,1,2], [0,1,2], 1)
Data.Data.add(test_curve_a, 0)
Data.Data.add(test_curve_b, 1)
with raises(InvalidIndex):
Data.Data.getC(2)
def test_add_IndepVarNotAscending_exception(self):
Data.Data.init()
test_curve_a = CurveT([-1], [-1], 1)
Data.Data.add(test_curve_a, 3)
with raises(IndepVarNotAscending):
Data.Data.add(test_curve_a, 1)
def test_eval(self):
Data.Data.init()
test_curve_a = CurveT([0,1,2,3], [0,1,2,3], 1)
test_curve_b = CurveT([0,1,2,3], [1,2,3,4], 1)
Data.Data.add(test_curve_a, 0)
Data.Data.add(test_curve_b, 1)
assert Data.Data.eval(1, 0) == approx(1, 0.005)
def test_eval_OutOfDomain_exception(self):
Data.Data.init()
test_curve_a = CurveT([0,1,2,3], [0,1,2,3], 1)
test_curve_b = CurveT([0,1,2,3], [1,2,3,4], 1)
Data.Data.add(test_curve_a, 0)
Data.Data.add(test_curve_b, 1)
with raises(OutOfDomain):
Data.Data.eval(0, 4)
def test_slice(self):
Data.Data.init()
test_curve_a = CurveT([0,1,2,3], [0,1,2,3], 1)
test_curve_b = CurveT([0,1,2,3], [1,2,3,4], 1)
Data.Data.add(test_curve_a, 0)
Data.Data.add(test_curve_b, 1)
new_curve = Data.Data.slice(1, 1)
assert new_curve.minD() == 0
assert new_curve.maxD() == 1
assert new_curve.order() == 1
class TestSeqServices:
def test_index(self):
assert SeqServices.index([1,2,3,4,5,6], 3.5) == 2
assert SeqServices.index([1,2], 1.5) == 0
def test_is_ascending_empty(self):
assert SeqServices.isAscending([])
def test_isAscending_unit(self):
assert SeqServices.isAscending([ 1 ])
def test_isAscending_two(self):
assert SeqServices.isAscending([ 1, 2 ])
def test_isAscending_repeat(self):
assert SeqServices.isAscending([ 0, 0, 0 ])
def test_isAscending_descending(self):
assert not SeqServices.isAscending([ 2, 1, 0 ])
def test_isInBounds(self):
assert SeqServices.isInBounds([ 1, 2 ], 1.5)
assert SeqServices.isInBounds([ 1, 2 ], 2)
assert SeqServices.isInBounds([ 1 ], 1)
def test_isInBounds_below(self):
assert not SeqServices.isInBounds([ 1, 2 ], 0.9999)
def test_isInBounds_In(self):
assert SeqServices.isInBounds([ 1, 2 ], 1.000)
assert SeqServices.isInBounds([ 1, 2 ], 1.00001)
assert SeqServices.isInBounds([ 1, 2 ], 1.9999)
assert SeqServices.isInBounds([ 1, 2 ], 1.5)
def test_isInBounds_above(self):
assert not SeqServices.isInBounds([ 1, 2 ], 2.0001)
def test_isInBounds_negative(self):
assert not SeqServices.isInBounds([ 1, 2 ], -1)
assert not SeqServices.isInBounds([ 1, 2 ], -2)
def test_interpLin(self):
assert SeqServices.interpLin(0, 1, 1, 2, 0.5) == 1.5
def test_interpQuad(self):
# -3.5 + 2.875x + 1.01786x^2
assert SeqServices.interpQuad(0, -3.5, 1, 0.39286, 2, 6.32144, 3) == approx(14.2857, 0.0001)
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment