Skip to content

Commit

Permalink
optimize kg_equal and add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
briangu committed Dec 21, 2023
1 parent 3d0bad7 commit cbccc58
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 51 deletions.
32 changes: 16 additions & 16 deletions klongpy/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def to_list(a):


def is_integer(x):
return issubclass(type(x), (int,np.integer))
return issubclass(type(x), (int, np.integer))


def is_float(x):
Expand Down Expand Up @@ -286,23 +286,23 @@ def kg_equal(a, b):
bool
True if the two inputs are equal, False otherwise.
"""
na = isinstance(a,np.ndarray)
nb = isinstance(b,np.ndarray)
la = isinstance(a,list)
lb = isinstance(b,list)
if na or la:
if (nb or lb) and (len(a) == len(b)):
if na and nb and a.dtype == b.dtype and a.dtype != 'O':
return np.array_equal(a,b)
for x, y in zip(a, b):
if not kg_equal(x, y):
return False
return True
return False
if nb or lb:
if a is b:
return True

na, nb = isinstance(a,np.ndarray), isinstance(b,np.ndarray)

if na and nb and a.dtype == b.dtype and a.dtype != 'O':
return np.array_equal(a,b)

na, nb = na or isinstance(a,list), nb or isinstance(b,list)

if na != nb:
return False
return np.isclose(a,b) if is_number(a) and is_number(b) else a == b

if na:
return len(a) == len(b) and all(kg_equal(x, y) for x, y in zip(a, b))

return np.isclose(a,b) if is_number(a) and is_number(b) else a == b

def has_none(a):
if isinstance(a,list):
Expand Down
18 changes: 7 additions & 11 deletions klongpy/sys_fn.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

import errno
import importlib
import importlib.util
import inspect
import os
Expand All @@ -10,14 +11,9 @@

import numpy

from .core import (np, KGChannel, KGChannelDir, KGSym, KGLambda, KlongException, is_empty,
is_list, is_dict, is_number, kg_read, kg_write, reserved_fn_args, reserved_fn_symbol_map, safe_eq)

import os
import sys
import importlib
from importlib.util import spec_from_file_location, module_from_spec
from inspect import signature, Parameter
from .core import (KGChannel, KGChannelDir, KGLambda, KGSym, KlongException,
is_dict, is_empty, is_list, kg_read, kg_write, np,
reserved_fn_args, reserved_fn_symbol_map, safe_eq)


def eval_sys_append_channel(x):
Expand Down Expand Up @@ -310,8 +306,8 @@ def import_file_module(x):
Import a module from a file path.
"""
module_name = os.path.dirname(x)
spec = spec_from_file_location(module_name, location=x)
module = module_from_spec(spec)
spec = importlib.util.spec_from_file_location(module_name, location=x)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module

Expand All @@ -322,7 +318,7 @@ def import_module_from_sys(x):
"""
spec = importlib.util.find_spec(x)
if spec is not None:
module = module_from_spec(spec)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
raise RuntimeError(f"module could not be imported: {x}")
Expand Down
46 changes: 23 additions & 23 deletions tests/gen_join_over.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import numpy as np
import subprocess
import os
import os
import itertools
import numpy as np


def _generate_arrays(depth, max_size, max_scalar):
arr = []
if depth == 0:
# Generate both scalars and arrays at leaf level
if max_size > 0:
arr.append(list(range(max_size)))
if max_scalar > 0:
arr.extend(list(range(max_scalar)))
else:
# Generate both scalars and arrays at each level
for sizes in itertools.product(range(max_size), repeat=max_size):
for sub_arrays in itertools.product(*[generate_arrays(depth - 1, size, max_scalar) for size in sizes]):
arr.append(list(sub_arrays)) # array
if max_scalar > 0:
arr.extend(list(range(max_scalar)))
for p in itertools.permutations(arr):
yield list(p)
# def _generate_arrays(depth, max_size, max_scalar):
# arr = []
# if depth == 0:
# # Generate both scalars and arrays at leaf level
# if max_size > 0:
# arr.append(list(range(max_size)))
# if max_scalar > 0:
# arr.extend(list(range(max_scalar)))
# else:
# # Generate both scalars and arrays at each level
# for sizes in itertools.product(range(max_size), repeat=max_size):
# for sub_arrays in itertools.product(*[generate_arrays(depth - 1, size, max_scalar) for size in sizes]):
# arr.append(list(sub_arrays)) # array
# if max_scalar > 0:
# arr.extend(list(range(max_scalar)))
# for p in itertools.permutations(arr):
# yield list(p)


def generate_random_arrays(depth, max_size, max_scalar, scalar_types=['%d','%s']):
Expand All @@ -45,10 +45,10 @@ def generate_random_arrays(depth, max_size, max_scalar, scalar_types=['%d','%s']
def run_test(test_case):
# Prepare the command and the input
cmd = [os.environ['KG_PATH'], "-e", test_case]

# Run the command with the input and capture the output
result = subprocess.run(cmd, text=True, capture_output=True)

# Return the result
return result.stdout

Expand All @@ -57,23 +57,23 @@ def fill_template(s):
params = []
i = 0
d = 0
while(i < len(s)):
while(i < len(s)):
if s[i] == '%':
i += 1
if s[i] == 'd':
params.append(d)
elif s[i] == 's':
params.append('"'+chr(ord('a')+(d % 26))+'"')
d += 1
i += 1
i += 1
s = s % tuple(params)
return s


def gen_tests(max_test_cases=10000, max_depth=3, max_size=3, max_scalar=3):
# Prepare all possible test cases
seen = set()
while len(seen) < max_test_cases:
while len(seen) < max_test_cases:
array = generate_random_arrays(np.random.randint(max_depth), max_size, max_scalar)
# Convert numpy array to Klong array string representation
array_str = (array if isinstance(array, str) else str(array)).replace(",","").replace("'",'"').replace('"%d"', '%d').replace('"%s"','%s')
Expand Down
8 changes: 8 additions & 0 deletions tests/kgtests/interop/test_pya.kg
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
:" access the len function of the Python object "

a:::{[1 2]}
len::.pya(a;"__len__")

t("a.__len__ == 1"; len(); 1)

t("a.__doc__ starts with dict"; 4#.pya(a;"__doc__"); "dict")
10 changes: 10 additions & 0 deletions tests/kgtests/interop/test_pya_numpy.kg
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
:" import the numpy.random module "
t(".pyf(""numpy"";""random"")"; .pyf("numpy";"random"); 1)

rns::.pya(random;"seed")
rns(0)
rnd::.pya(random;"random")

expected::[[0.5488135039273248 0.7151893663724195] [0.6027633760716439 0.5448831829968969] [0.4236547993389047 0.6458941130666561] [0.4375872112626925 0.8917730007820798]]

t("rnd([4 2])=expected"; rnd([4 2]); expected)
2 changes: 1 addition & 1 deletion tests/kgtests/interop/test_pyc_numpy.kg
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
t(".pyf(""numpy"";""mean"")"; .pyf("numpy";"mean"); 1)

:" this should fail "
:" use the numpy mean function "
t("mean(!100)"; mean(!100); 49.5)
38 changes: 38 additions & 0 deletions tests/perf_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import glob
import math
import os
import unittest

from utils import *

import timeit

def parse_suite_file(fname, number=10):
klong = KlongInterpreter()
with open(fname, "r") as f:
d = f.read()
b = len(d) * number
r = timeit.timeit(lambda: klong(d), number=number)
return b, r, int(b / r), r / number


def test_kgtests():
"""
Recursively run all tests under the kgtests folder that begin with "test" and end with ".kg".
"""
root_dir = os.path.join(os.getcwd(), "tests", "kgtests")

for dirpath, _, filenames in os.walk(root_dir):
for fname in filenames:
if (fname.startswith("gen")) and fname.endswith(".kg"):
ran_tests = True
klong = KlongInterpreter()
fullpath = os.path.join(dirpath, fname)
klong['fullpath'] = fullpath
klong('.l("tests/kgtests/runner.kg")')
if fname.startswith("gen"):
r = parse_suite_file(fullpath)
print(f"total: {r[1]} iter: {r[-1]}")

if __name__ == "__main__":
test_kgtests()
3 changes: 3 additions & 0 deletions tests/perf_join.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ def perf_read_string(s, number=1000):
return kr


"""
some basic perf test for python join operator.
"""

if __name__ == "__main__":
perf_join(100,10000)
Expand Down

0 comments on commit cbccc58

Please sign in to comment.