Joseph Mariadassou
3 min readFeb 7, 2023

--

CoPilot is No Threat to a Good Programmer

Photo by John Schnobrich on Unsplash

Prerequisite: Some knowledge of Python and C++

A lot is being said about ChatGPT. But the older kid around the block is Github CoPilot. I asked CoPilot to compute the internal rate of return using the comment:
Compute the internal rate of return of a series of cash flows (irr)

The last phrase ‘(irr)’ was generated by VSCode with the help of CoPilot (CP), I presume. That by itself was pretty impressive. I got CoPilot to produce C++ and Python code. It generated three different C++ functions on the three days; only one of which was is correct as shown below:

/*
Compute the net present value of a series of cash flows npv
*/
double npv(double *cashflows, int n, double r)
{
double npv = 0.0;
double r1 = 1.0;
for (int i = 0; i < n; i++)
{
npv += cashflows[i] / r1;
r1 = r1 * (1.0 + r);
}
return npv;
}
/*
Compute the internal rate of return of a series of cash flows (irr)
*/
double irr(double *cashflows, int n)
{
double x0, x1, f0, f1, df;
int i;
x0 = 0.0;
f0 = 0.0;
for (i = 0; i < n; i++) {
f0 += cashflows[i] ;
}
x1 = 0.1;
f1 = npv(cashflows, n, x1 );
while (fabs(f1) > 1.0e-6)
{
df = (f1 - f0) / (x1 - x0);
x0 = x1;
f0 = f1;
x1 = x0 - f0 / df;
f1 = npv(cashflows, n, x1 );
}
return x1;
}

I got CoPilot to generate npv and refactored irr to use it. Of the two other wrong codes one of them is obviously wrong. Here is the version CoPilot generated

/*
Compute the net present value of a series of cash flows
*/
double npv(std::vector<double>& cashFlows, double discountRate) {
double npv = 0.0;
for (int i = 0; i < cashFlows.size(); i++) {
npv += cashFlows[i] / std::pow(1 + discountRate, i);
}
return npv;
}
/*
Compute the internal rate of return of a series of cash flows (irr)
*/
double irr(std::vector<double>& cashFlows) {
double guess = 0.1;
double npv = 0.0;
double epsilon = 0.0000001;
while (true) {
npv = 0.0;
for (int i = 0; i < cashFlows.size(); i++) {
npv += cashFlows[i] / std::pow(1 + guess, i);
}
if (std::abs(npv) < epsilon) {
break;
}
guess += 0.0001;
}
return guess;
}

Notice that npv could be optimised to the previous version. At least the function is a starting point in that it can be tweaked to compute a value most of the time. The third version is one I could not make sense of:

/*
Compute the internal rate of return of a series of cash flows (irr)
*/
double irr(const std::vector<double>& cashflows, double guess)
{
double x1 = guess;
double x0 = 0.0;
double f1 = 0.0;
double f0 = 0.0;
int max_iter = 100;
int iter = 0;
double eps = 1e-6;
double dx = 1e-6;
double df = 0.0;
double f = 0.0;
double x = 0.0;
int i = 0;
int n = cashflows.size();
while (iter < max_iter)
{
f1 = 0.0;
f0 = 0.0;
for (i = 0; i < n; i++)
{
f1 += cashflows[i] / pow(1.0 + x1, i);
f0 += cashflows[i] / pow(1.0 + x0, i);
}
df = (f1 - f0) / dx;
f = f1 + (x1 - x0) * f1 / df;
x = x1 - f1 / df;
if (fabs(f) < eps)
{
break;
}
x0 = x1;
x1 = x;
iter++;
}
return x;
}

The first Python version was bad in that it usually will not converge:

"""
Compute the net present value of a series of cash flows
"""
def npv(cashflows, rate):
npv = 0.0
for i in range(len(cashflows)):
npv += cashflows[i]/(1+rate)**i
return npv

"""
Compute the internal rate of return of a series of cash flows (irr)
"""
def irr(cashflows):
rate = 1.0/8
def redeem(rate):
return npv(cashflows, rate)
redeem_value = redeem(rate)
increment = 1.0/4096.0

print("r=", rate)
print("i=", increment)
while abs(redeem_value) > 0.01:
if redeem_value < 0 :
rate = rate + increment
else:
rate = rate - increment
redeem_value = redeem(rate)
#print("r=", rate)
return rate

The second version though is a perfect one-liner:

def irr(cashflows):
return np.irr(cashflows)

I was not aware Numpy had such a built-in function.

In sum, CoPilot does produce good code sometimes. But it is not reliable

--

--

Joseph Mariadassou

Software developer with interest in Politics, Philosophy and Economics