Skip to content

Fmean, mean overload functions implemented #877

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

Merged
merged 5 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,4 @@ RUN(NAME test_str_comparison LABELS cpython llvm)

RUN(NAME generics_01 LABELS cpython llvm)
RUN(NAME generics_array_01 LABELS llvm)

# Just CPython
RUN(NAME test_statistics LABELS cpython)
RUN(NAME test_statistics LABELS cpython llvm)
24 changes: 22 additions & 2 deletions integration_tests/test_statistics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from statistics import (mean, geometric_mean, harmonic_mean)
from statistics import (mean, geometric_mean, harmonic_mean, fmean)
from ltypes import i32, f64, i64

eps: f64
Expand All @@ -17,6 +17,26 @@ def test_mean():
k = mean(c)
assert abs(k - 5.4) < eps

d: list[i32]
d = [1, 3, 11]
l: f64
l = mean(d)
assert abs(l - 5) < eps

def test_fmean():
a: list[i32]
a = [9, 4, 10]
j: f64
j = fmean(a)
assert abs(j - 7.666666666666667) < eps

b: list[f64]
b = [-1.9, -13.8, -6.0, 4.2, 5.9, 9.1]
k: f64
k = fmean(b)
assert abs(k + 0.41666666666666674) < eps


def test_geometric_mean():
c: list[i32]
c = [1,2,3]
Expand All @@ -31,10 +51,10 @@ def test_harmonic_mean():
k = harmonic_mean(c)
assert abs(k - 4.740458015267175) < eps


def check():
test_mean()
test_geometric_mean()
test_harmonic_mean()
test_fmean()

check()
5 changes: 5 additions & 0 deletions src/libasr/asr_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,11 @@ bool types_equal(const ASR::ttype_t &a, const ASR::ttype_t &b) {
}
break;
}
case (ASR::ttypeType::List) : {
ASR::List_t *a2 = ASR::down_cast<ASR::List_t>(&a);
ASR::List_t *b2 = ASR::down_cast<ASR::List_t>(&b);
return types_equal(*a2->m_type, *b2->m_type);
}
case (ASR::ttypeType::Derived) : {
ASR::Derived_t *a2 = ASR::down_cast<ASR::Derived_t>(&a);
ASR::Derived_t *b2 = ASR::down_cast<ASR::Derived_t>(&b);
Expand Down
14 changes: 14 additions & 0 deletions src/libasr/codegen/asr_to_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2195,6 +2195,20 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
type = tuple_api->get_tuple_type(type_code, llvm_el_types)->getPointerTo();
break;
}
case (ASR::ttypeType::List) : {
bool is_array_type = false, is_malloc_array_type = false;
bool is_list = true;
ASR::dimension_t *m_dims = nullptr;
ASR::List_t* asr_list = ASR::down_cast<ASR::List_t>(asr_type);
llvm::Type* el_llvm_type = get_type_from_ttype_t(asr_list->m_type, m_storage,
is_array_type,
is_malloc_array_type,
is_list, m_dims, n_dims,
a_kind);
std::string el_type_code = ASRUtils::get_type_code(asr_list->m_type);
type = list_api->get_list_type(el_llvm_type, el_type_code, a_kind)->getPointerTo();
break;
}
default :
LFORTRAN_ASSERT(false);
}
Expand Down
111 changes: 92 additions & 19 deletions src/runtime/statistics.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from ltypes import i32, f64
from ltypes import i32, f64, overload


@overload
def mean(x: list[i32]) -> f64:
"""
Returns the arithmetic mean of a data sequence of numbers
"""
k: i32 = len(x)
if k == 0:
return 0.0
Expand All @@ -11,54 +15,123 @@ def mean(x: list[i32]) -> f64:

for i in range(k):
sum += x[i]
ans: f64
ans = sum/k
return ans
return sum/k


@overload
def mean(x: list[i64]) -> f64:
"""
Returns the arithmetic mean of a data sequence of numbers
"""
k: i32 = len(x)
if k == 0:
return 0.0
sum: f64
sum = 0.0
i: i32

for i in range(k):
sum += x[i]

return sum/k


@overload
def mean(x: list[f32]) -> f64:
"""
Returns the arithmetic mean of a data sequence of numbers
"""
k: i32 = len(x)
if k == 0:
return 0.0
sum: f64
sum = 0.0
i: i32

for i in range(k):
sum += x[i]
return sum/k
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, these are great candidates to try using the new templates. The templates are still experimental, and we still need to implement restrictions, so things will change, but the current state should be able to compile such functions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. @ansharlubis @Oshanath May be, you can try templates on these functions and reduce the implementation size? What do you folks say?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be a little bit early, since it will slow @ansharlubis down if we have to do any refactoring, which we will. We can however do it on a branch / Draft PR that is not merged. That would be great, just to see if it works, and to iron out any bugs, but I would not merge into master yet.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It kind of work in an example I added in PR #921, it still uses the hard-coded restrictions though and don't do much type checks.



@overload
def mean(x: list[f64]) -> f64:
"""
Returns the arithmetic mean of a data sequence of numbers
"""
k: i32 = len(x)
if k == 0:
return 0.0
sum: f64
sum = 0.0
i: i32

for i in range(k):
sum += x[i]
ans: f64
ans = sum/k
return ans
return sum/k


@overload
def fmean(x: list[i32]) -> f64:
"""
Returns the floating type arithmetic mean of a data sequence of numbers
"""
return mean(x)


@overload
def fmean(x: list[i64]) -> f64:
"""
Returns the floating type arithmetic mean of a data sequence of numbers
"""
return mean(x)


@overload
def fmean(x: list[f64]) -> f64:
"""
Returns the floating type arithmetic mean of a data sequence of numbers
"""
return mean(x)


@overload
def fmean(x: list[f32]) -> f64:
"""
Returns the floating type arithmetic mean of a data sequence of numbers
"""
return mean(x)


def geometric_mean(x: list[i32]) -> f64:
"""
Returns the geometric mean of a data sequence of numbers
"""
k: i32 = len(x)
if k == 0:
return 0.0
product: f64
product = 1.0
i: i32

for i in range(k):
if x[i] < 1:
raise ValueError('geometric mean requires a non-empty dataset containing positive numbers')
product *= x[i]
ans: f64
ans = product**(1/k)
return ans

@overload
return product**(1/k)


def harmonic_mean(x: list[i32]) -> f64:
"""
Returns the harmonic mean of a data sequence of numbers
"""
k: i32 = len(x)
if k == 0:
return 0.0
sum: f64
sum = 0.0
i: i32

for i in range(k):
if x[i] < 0:
raise ValueError('harmonic mean does not support negative values')
if x[i] ==0:
if x[i] == 0:
return 0.0
sum += 1 / x[i]
ans: f64
ans = k/sum
return ans
return k/sum