From d2d6ba09c8d0d5d37a447f06f56ef3e67fd95e90 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Tue, 23 Jan 2024 19:25:26 +0300 Subject: [PATCH 1/8] Rust2Metta gnd conversion --- c/src/atom.rs | 17 +++++++++++++++++ c/src/metta.rs | 18 ++++++++++++++++++ lib/Cargo.toml | 1 + lib/src/metta/runner/arithmetics.rs | 28 ++++++++++++++++++++++++++++ lib/src/metta/runner/stdlib.rs | 5 +++++ python/hyperon/atoms.py | 18 +++++++++++++----- python/hyperonpy.cpp | 11 +++++++++++ python/sandbox/test_gnd_conv.metta | 11 +++++++++++ 8 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 python/sandbox/test_gnd_conv.metta diff --git a/c/src/atom.rs b/c/src/atom.rs index 2a2456c72..7958c5c40 100644 --- a/c/src/atom.rs +++ b/c/src/atom.rs @@ -435,6 +435,23 @@ pub unsafe extern "C" fn atom_get_object(atom: *const atom_ref_t) -> *mut gnd_t } } +/// @brief Check if the atom refers to CGrounded object +/// @ingroup atom_group +/// @param[in] atom A pointer to an `atom_t` or an `atom_ref_t` to access +/// @return True if the atom_get_object can be used on this atom without panic +/// +#[no_mangle] +pub unsafe extern "C" fn atom_is_cgrounded(atom: *const atom_ref_t) -> bool { + if let Atom::Grounded(ref g) = (&*atom).borrow() { + match (*g).as_any_ref().downcast_ref::() { + Some(_g) => true, + None => false, + } + } else { + false + } +} + /// @brief Access the space wrapped inside a Grounded atom /// @ingroup atom_group /// @see atom_gnd_for_space diff --git a/c/src/metta.rs b/c/src/metta.rs index c03ec1c83..4041e6bf2 100644 --- a/c/src/metta.rs +++ b/c/src/metta.rs @@ -1412,6 +1412,24 @@ pub extern "C" fn grounded_number_to_longlong(n: *const atom_t, res: *mut c_long } } +#[no_mangle] +pub extern "C" fn grounded_bool_to_int(n: *const atom_t, res: *mut c_int) -> bool { + // NOTE: there is no c_bool, so we have to choose particular int type + let atom = unsafe { (*n).borrow() }; + match atom { + Atom::Grounded(gnd) => { + match gnd.as_any_ref().downcast_ref::() { + Some(Bool(b)) => { + unsafe { *res = if *b { 1 } else { 0 } }; + true + } + _ => false, + } + }, + _ => false, + } +} + #[no_mangle] pub extern "C" fn grounded_number_to_double(n: *const atom_t, res: *mut c_double) -> bool { let atom = unsafe { (*n).borrow() }; diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 38d072bb1..7e5f17d60 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -12,6 +12,7 @@ env_logger = "0.8.4" directories = "5.0.1" # For Environment to find platform-specific config location smallvec = "1.10.0" im = "15.1.0" +rand = "0.8.5" [lib] name = "hyperon" diff --git a/lib/src/metta/runner/arithmetics.rs b/lib/src/metta/runner/arithmetics.rs index c46eaf01e..f3e30b541 100644 --- a/lib/src/metta/runner/arithmetics.rs +++ b/lib/src/metta/runner/arithmetics.rs @@ -193,6 +193,34 @@ macro_rules! def_binary_bool_op { def_binary_bool_op!(AndOp, and, &&); def_binary_bool_op!(OrOp, or, ||); +// NOTE: xor and flip are absent in Python intentionally for conversion testing +def_binary_bool_op!(XorOp, xor, ^); + +use rand; +#[derive(Clone, PartialEq, Debug)] +pub struct FlipOp{} + +impl Display for FlipOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "flip") + } +} + +impl Grounded for FlipOp { + fn type_(&self) -> Atom { + Atom::expr([ARROW_SYMBOL, ATOM_TYPE_BOOL]) + } + + fn execute(&self, _args: &[Atom]) -> Result, ExecError> { + Ok(vec![Atom::gnd(Bool(rand::random()))]) + } + + fn match_(&self, other: &Atom) -> MatchResultIter { + match_by_equality(self, other) + } +} + + #[derive(Clone, PartialEq, Debug)] pub struct NotOp{} diff --git a/lib/src/metta/runner/stdlib.rs b/lib/src/metta/runner/stdlib.rs index a139d2507..e3e91e192 100644 --- a/lib/src/metta/runner/stdlib.rs +++ b/lib/src/metta/runner/stdlib.rs @@ -1253,6 +1253,11 @@ pub fn register_rust_tokens(metta: &Metta) { tref.register_token(regex(r"or"), move |_| { or_op.clone() }); let not_op = Atom::gnd(NotOp{}); tref.register_token(regex(r"not"), move |_| { not_op.clone() }); + // NOTE: xor and flip are absent in Python intentionally for conversion testing + let xor_op = Atom::gnd(XorOp{}); + tref.register_token(regex(r"xor"), move |_| { xor_op.clone() }); + let flip_op = Atom::gnd(FlipOp{}); + tref.register_token(regex(r"flip"), move |_| { flip_op.clone() }); metta.tokenizer().borrow_mut().move_front(&mut rust_tokens); } diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 6fbd3ca7f..43e430085 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -138,12 +138,20 @@ def __init__(self, catom): super().__init__(catom) def get_object(self): - """Returns the GroundedAtom object, or the Space wrapped inside a GroundedAtom""" - from .base import SpaceRef - if self.get_grounded_type() == AtomType.GROUNDED_SPACE: - return SpaceRef._from_cspace(hp.atom_get_space(self.catom)) - else: + """Returns the GroundedAtom object, or the Space wrapped inside a GroundedAtom, + or convert supported Rust grounded objects into corresponding ValueObjects + """ + if hp.atom_is_cgrounded(self.catom): return hp.atom_get_object(self.catom) + typ = self.get_grounded_type() + if typ == AtomType.GROUNDED_SPACE: + from .base import SpaceRef + return SpaceRef._from_cspace(hp.atom_get_space(self.catom)) + # NOTE: Rust and Python may have the same grounded type names, but we already + # distiguished them above + elif typ == S('Bool'): + return ValueObject(hp.gnd_to_bool(self.catom)) + raise TypeError("Cannot get_object of unsupported non-C {self.catom}") def get_grounded_type(self): """Retrieve the grounded type of the GroundedAtom.""" diff --git a/python/hyperonpy.cpp b/python/hyperonpy.cpp index ef7dd86b5..f486b2ff3 100644 --- a/python/hyperonpy.cpp +++ b/python/hyperonpy.cpp @@ -541,6 +541,9 @@ PYBIND11_MODULE(hyperonpy, m) { m.def("atom_get_object", [](CAtom& atom) { return static_cast(atom_get_object(atom.ptr()))->pyobj; }, "Get object of the grounded atom"); + m.def("atom_is_cgrounded", [](CAtom& atom) { + return py::bool_(atom_is_cgrounded(atom.ptr())); + }, "Check if atom is CGrounded"); m.def("atom_get_grounded_type", [](CAtom& atom) { return CAtom(atom_get_grounded_type(atom.ptr())); }, "Get object of the grounded atom"); @@ -867,6 +870,14 @@ PYBIND11_MODULE(hyperonpy, m) { } else return py::none(); }, "Convert MeTTa stdlib number to Python int"); + m.def("gnd_to_bool", [](CAtom atom) -> py::object { + // NOTE: we convert Rust bool to C++ int, which is converted to bool + int n; + if (grounded_bool_to_int(atom.ptr(), &n)) { + return py::bool_(n); + } else + return py::none(); + }, "Convert MeTTa-Rust bool to Python bool"); m.def("gnd_to_float", [](CAtom atom) -> py::object { double d; if (grounded_number_to_double(atom.ptr(), &d)) diff --git a/python/sandbox/test_gnd_conv.metta b/python/sandbox/test_gnd_conv.metta new file mode 100644 index 000000000..262f704ff --- /dev/null +++ b/python/sandbox/test_gnd_conv.metta @@ -0,0 +1,11 @@ +; should work as passing Rust Bool to Rust function +!(xor (flip) (flip)) + +; should work via automatic Rust->Python conversion +!(and (flip) (flip)) + +; should work as well +!(and (flip) True) + +; Should not work atm because of no backward conversion of Python Bool to Rust +!(xor True (flip)) From d4e52831a64c3c18664ed8d63d6863f2aeeb7861 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Wed, 24 Jan 2024 12:38:39 +0300 Subject: [PATCH 2/8] Update c/src/metta.rs Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- c/src/metta.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/src/metta.rs b/c/src/metta.rs index 4041e6bf2..bb03448d6 100644 --- a/c/src/metta.rs +++ b/c/src/metta.rs @@ -1413,7 +1413,7 @@ pub extern "C" fn grounded_number_to_longlong(n: *const atom_t, res: *mut c_long } #[no_mangle] -pub extern "C" fn grounded_bool_to_int(n: *const atom_t, res: *mut c_int) -> bool { +pub extern "C" fn grounded_bool_to_bool(n: *const atom_ref_t, res: *mut bool) -> bool { // NOTE: there is no c_bool, so we have to choose particular int type let atom = unsafe { (*n).borrow() }; match atom { From 48022a4392c476739bfb9c2a6d648367c8d06644 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Wed, 24 Jan 2024 12:38:48 +0300 Subject: [PATCH 3/8] Update c/src/metta.rs Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- c/src/metta.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/src/metta.rs b/c/src/metta.rs index bb03448d6..7a96387af 100644 --- a/c/src/metta.rs +++ b/c/src/metta.rs @@ -1420,7 +1420,7 @@ pub extern "C" fn grounded_bool_to_bool(n: *const atom_ref_t, res: *mut bool) -> Atom::Grounded(gnd) => { match gnd.as_any_ref().downcast_ref::() { Some(Bool(b)) => { - unsafe { *res = if *b { 1 } else { 0 } }; + unsafe { *res = *b }; true } _ => false, From 5888eeeb22286c71768659ed8f6eddb718ac80ae Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Wed, 24 Jan 2024 12:39:03 +0300 Subject: [PATCH 4/8] Update c/src/metta.rs Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- c/src/metta.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/c/src/metta.rs b/c/src/metta.rs index 7a96387af..641bcf2be 100644 --- a/c/src/metta.rs +++ b/c/src/metta.rs @@ -1411,7 +1411,12 @@ pub extern "C" fn grounded_number_to_longlong(n: *const atom_t, res: *mut c_long _ => false, } } - +/// @brief Access the value of a grounded bool atom +/// @ingroup metta_language_group +/// @param[in] n A pointer to an `atom_t` or an `atom_ref_t` to access +/// @param[out] res A pointer to the variable into which to write the result +/// @return True if the atom was a grounded bool atom, and the result was successfully written +/// #[no_mangle] pub extern "C" fn grounded_bool_to_bool(n: *const atom_ref_t, res: *mut bool) -> bool { // NOTE: there is no c_bool, so we have to choose particular int type From 4a6320594ddd2cddb4128f8cab7f3b7640b0ccb1 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Wed, 24 Jan 2024 12:39:30 +0300 Subject: [PATCH 5/8] Update python/hyperonpy.cpp Co-authored-by: luketpeterson <36806965+luketpeterson@users.noreply.github.com> --- python/hyperonpy.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/hyperonpy.cpp b/python/hyperonpy.cpp index f486b2ff3..aaa0de589 100644 --- a/python/hyperonpy.cpp +++ b/python/hyperonpy.cpp @@ -871,7 +871,9 @@ PYBIND11_MODULE(hyperonpy, m) { return py::none(); }, "Convert MeTTa stdlib number to Python int"); m.def("gnd_to_bool", [](CAtom atom) -> py::object { - // NOTE: we convert Rust bool to C++ int, which is converted to bool + bool b; + if (grounded_bool_to_bool(atom.ptr(), &b)) { + return py::bool_(b); int n; if (grounded_bool_to_int(atom.ptr(), &n)) { return py::bool_(n); From 53553071956abb98ecd7069eda9cfa46ac2a6f40 Mon Sep 17 00:00:00 2001 From: Alexey Potapov Date: Wed, 24 Jan 2024 12:42:18 +0300 Subject: [PATCH 6/8] int -> bool --- python/hyperonpy.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/python/hyperonpy.cpp b/python/hyperonpy.cpp index aaa0de589..3f984a5c1 100644 --- a/python/hyperonpy.cpp +++ b/python/hyperonpy.cpp @@ -874,9 +874,6 @@ PYBIND11_MODULE(hyperonpy, m) { bool b; if (grounded_bool_to_bool(atom.ptr(), &b)) { return py::bool_(b); - int n; - if (grounded_bool_to_int(atom.ptr(), &n)) { - return py::bool_(n); } else return py::none(); }, "Convert MeTTa-Rust bool to Python bool"); From 3a49aff93e765c634a1aeb5e9c79cce3ea5148d0 Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Thu, 25 Jan 2024 10:03:19 +0300 Subject: [PATCH 7/8] Fix grounded_number... function signatures Replace *const atom_t by *const atom_ref_t as functions input references to the atoms. Add docstrings. --- c/src/metta.rs | 16 +++++++++++++--- python/tests/test_grounded_type.py | 3 +-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/c/src/metta.rs b/c/src/metta.rs index 641bcf2be..f4b8fd969 100644 --- a/c/src/metta.rs +++ b/c/src/metta.rs @@ -1395,8 +1395,13 @@ pub extern "C" fn env_builder_add_include_path(builder: *mut env_builder_t, path *builder_arg_ref = builder.into(); } +/// @brief Access the value of a grounded i64 atom +/// @ingroup metta_language_group +/// @param[in] n A pointer to an `atom_t` or an `atom_ref_t` to access +/// @param[out] res A pointer to the variable into which to write the result +/// @return True if the atom was a grounded i64 atom, and the result was successfully written #[no_mangle] -pub extern "C" fn grounded_number_to_longlong(n: *const atom_t, res: *mut c_longlong) -> bool { +pub extern "C" fn grounded_number_to_longlong(n: *const atom_ref_t, res: *mut c_longlong) -> bool { let atom = unsafe { (*n).borrow() }; match atom { Atom::Grounded(gnd) => { @@ -1411,12 +1416,12 @@ pub extern "C" fn grounded_number_to_longlong(n: *const atom_t, res: *mut c_long _ => false, } } + /// @brief Access the value of a grounded bool atom /// @ingroup metta_language_group /// @param[in] n A pointer to an `atom_t` or an `atom_ref_t` to access /// @param[out] res A pointer to the variable into which to write the result /// @return True if the atom was a grounded bool atom, and the result was successfully written -/// #[no_mangle] pub extern "C" fn grounded_bool_to_bool(n: *const atom_ref_t, res: *mut bool) -> bool { // NOTE: there is no c_bool, so we have to choose particular int type @@ -1435,8 +1440,13 @@ pub extern "C" fn grounded_bool_to_bool(n: *const atom_ref_t, res: *mut bool) -> } } +/// @brief Access the value of a grounded f64 atom +/// @ingroup metta_language_group +/// @param[in] n A pointer to an `atom_t` or an `atom_ref_t` to access +/// @param[out] res A pointer to the variable into which to write the result +/// @return True if the atom was a grounded f64 atom, and the result was successfully written #[no_mangle] -pub extern "C" fn grounded_number_to_double(n: *const atom_t, res: *mut c_double) -> bool { +pub extern "C" fn grounded_number_to_double(n: *const atom_ref_t, res: *mut c_double) -> bool { let atom = unsafe { (*n).borrow() }; match atom { Atom::Grounded(gnd) => { diff --git a/python/tests/test_grounded_type.py b/python/tests/test_grounded_type.py index 1a9ad864a..369874d6a 100644 --- a/python/tests/test_grounded_type.py +++ b/python/tests/test_grounded_type.py @@ -150,9 +150,8 @@ def test_number_conversion(self): self.assertEqual(type(num), GroundedAtom) self.assertTrue(abs(hp.gnd_to_float(num.catom) - 123.456) < 0.0001) - sym = S("sym") self.assertEqual(hp.gnd_to_int(S("sym").catom), None) - self.assertEqual(hp.gnd_to_float(sym.catom), None) + self.assertEqual(hp.gnd_to_float(S("sym").catom), None) if __name__ == "__main__": unittest.main() From 07af9a3974663d75618b7748b9d9ef46d2df3c8b Mon Sep 17 00:00:00 2001 From: Vitaly Bogdanov Date: Thu, 25 Jan 2024 10:09:54 +0300 Subject: [PATCH 8/8] Rename _to_ functions to _get_ and _into_ _into_ infix is used for consuming functions, _get_ for non-consuming --- c/src/metta.rs | 10 +++++----- python/hyperon/atoms.py | 2 +- python/hyperonpy.cpp | 18 +++++++++--------- python/tests/test_grounded_type.py | 12 ++++++------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/c/src/metta.rs b/c/src/metta.rs index f4b8fd969..29f0ed9bf 100644 --- a/c/src/metta.rs +++ b/c/src/metta.rs @@ -1401,7 +1401,7 @@ pub extern "C" fn env_builder_add_include_path(builder: *mut env_builder_t, path /// @param[out] res A pointer to the variable into which to write the result /// @return True if the atom was a grounded i64 atom, and the result was successfully written #[no_mangle] -pub extern "C" fn grounded_number_to_longlong(n: *const atom_ref_t, res: *mut c_longlong) -> bool { +pub extern "C" fn grounded_number_get_longlong(n: *const atom_ref_t, res: *mut c_longlong) -> bool { let atom = unsafe { (*n).borrow() }; match atom { Atom::Grounded(gnd) => { @@ -1423,7 +1423,7 @@ pub extern "C" fn grounded_number_to_longlong(n: *const atom_ref_t, res: *mut c_ /// @param[out] res A pointer to the variable into which to write the result /// @return True if the atom was a grounded bool atom, and the result was successfully written #[no_mangle] -pub extern "C" fn grounded_bool_to_bool(n: *const atom_ref_t, res: *mut bool) -> bool { +pub extern "C" fn grounded_bool_get_bool(n: *const atom_ref_t, res: *mut bool) -> bool { // NOTE: there is no c_bool, so we have to choose particular int type let atom = unsafe { (*n).borrow() }; match atom { @@ -1446,7 +1446,7 @@ pub extern "C" fn grounded_bool_to_bool(n: *const atom_ref_t, res: *mut bool) -> /// @param[out] res A pointer to the variable into which to write the result /// @return True if the atom was a grounded f64 atom, and the result was successfully written #[no_mangle] -pub extern "C" fn grounded_number_to_double(n: *const atom_ref_t, res: *mut c_double) -> bool { +pub extern "C" fn grounded_number_get_double(n: *const atom_ref_t, res: *mut c_double) -> bool { let atom = unsafe { (*n).borrow() }; match atom { Atom::Grounded(gnd) => { @@ -1463,11 +1463,11 @@ pub extern "C" fn grounded_number_to_double(n: *const atom_ref_t, res: *mut c_do } #[no_mangle] -pub extern "C" fn longlong_to_grounded_number(n: c_longlong) -> atom_t { +pub extern "C" fn longlong_into_grounded_number(n: c_longlong) -> atom_t { Atom::gnd(Number::Integer(n)).into() } #[no_mangle] -pub extern "C" fn double_to_grounded_number(d: c_double) -> atom_t { +pub extern "C" fn double_into_grounded_number(d: c_double) -> atom_t { Atom::gnd(Number::Float(d)).into() } diff --git a/python/hyperon/atoms.py b/python/hyperon/atoms.py index 43e430085..ed592284d 100644 --- a/python/hyperon/atoms.py +++ b/python/hyperon/atoms.py @@ -150,7 +150,7 @@ def get_object(self): # NOTE: Rust and Python may have the same grounded type names, but we already # distiguished them above elif typ == S('Bool'): - return ValueObject(hp.gnd_to_bool(self.catom)) + return ValueObject(hp.gnd_get_bool(self.catom)) raise TypeError("Cannot get_object of unsupported non-C {self.catom}") def get_grounded_type(self): diff --git a/python/hyperonpy.cpp b/python/hyperonpy.cpp index 3f984a5c1..94dc0e0e6 100644 --- a/python/hyperonpy.cpp +++ b/python/hyperonpy.cpp @@ -863,33 +863,33 @@ PYBIND11_MODULE(hyperonpy, m) { m.def("env_builder_set_is_test", [](EnvBuilder& builder, bool is_test) { env_builder_set_is_test(builder.ptr(), is_test); }, "Disables the config dir in the environment"); m.def("env_builder_add_include_path", [](EnvBuilder& builder, std::string path) { env_builder_add_include_path(builder.ptr(), path.c_str()); }, "Adds an include path to the environment"); - m.def("gnd_to_int", [](CAtom atom) -> py::object { + m.def("gnd_get_int", [](CAtom atom) -> py::object { long long n; - if (grounded_number_to_longlong(atom.ptr(), &n)) { + if (grounded_number_get_longlong(atom.ptr(), &n)) { return py::int_(n); } else return py::none(); }, "Convert MeTTa stdlib number to Python int"); - m.def("gnd_to_bool", [](CAtom atom) -> py::object { + m.def("gnd_get_bool", [](CAtom atom) -> py::object { bool b; - if (grounded_bool_to_bool(atom.ptr(), &b)) { + if (grounded_bool_get_bool(atom.ptr(), &b)) { return py::bool_(b); } else return py::none(); }, "Convert MeTTa-Rust bool to Python bool"); - m.def("gnd_to_float", [](CAtom atom) -> py::object { + m.def("gnd_get_float", [](CAtom atom) -> py::object { double d; - if (grounded_number_to_double(atom.ptr(), &d)) + if (grounded_number_get_double(atom.ptr(), &d)) return py::float_(d); else return py::none(); }, "Convert MeTTa stdlib number to Python float"); - m.def("number_to_gnd", [](py::object n) { + m.def("number_into_gnd", [](py::object n) { if (py::isinstance(n)) { - return CAtom(longlong_to_grounded_number(n.cast())); + return CAtom(longlong_into_grounded_number(n.cast())); } if (py::isinstance(n)) { - return CAtom(double_to_grounded_number(n.cast())); + return CAtom(double_into_grounded_number(n.cast())); } throw std::runtime_error("int of float number is expected as an argument"); }, "Convert Python number to MeTTa stdlib number"); diff --git a/python/tests/test_grounded_type.py b/python/tests/test_grounded_type.py index 369874d6a..abe155631 100644 --- a/python/tests/test_grounded_type.py +++ b/python/tests/test_grounded_type.py @@ -142,16 +142,16 @@ def test_undefined_operation_type(self): metta.parse_single("untyped").get_grounded_type()) def test_number_conversion(self): - num = Atom._from_catom(hp.number_to_gnd(123)) + num = Atom._from_catom(hp.number_into_gnd(123)) self.assertEqual(type(num), GroundedAtom) - self.assertEqual(hp.gnd_to_int(num.catom), 123) + self.assertEqual(hp.gnd_get_int(num.catom), 123) - num = Atom._from_catom(hp.number_to_gnd(123.456)) + num = Atom._from_catom(hp.number_into_gnd(123.456)) self.assertEqual(type(num), GroundedAtom) - self.assertTrue(abs(hp.gnd_to_float(num.catom) - 123.456) < 0.0001) + self.assertTrue(abs(hp.gnd_get_float(num.catom) - 123.456) < 0.0001) - self.assertEqual(hp.gnd_to_int(S("sym").catom), None) - self.assertEqual(hp.gnd_to_float(S("sym").catom), None) + self.assertEqual(hp.gnd_get_int(S("sym").catom), None) + self.assertEqual(hp.gnd_get_float(S("sym").catom), None) if __name__ == "__main__": unittest.main()