Skip to content

Commit

Permalink
unary op preserve tensor topology
Browse files Browse the repository at this point in the history
  • Loading branch information
dragazo committed Nov 29, 2023
1 parent 4129b7a commit 8f494ab
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 42 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "netsblox-vm"
version = "0.3.3"
version = "0.3.4"
edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["Devin Jean <emailcruzjean@yahoo.com>"]
Expand Down
85 changes: 44 additions & 41 deletions src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1407,6 +1407,12 @@ impl<'gc, C: CustomTypes<S>, S: System<C>> Process<'gc, C, S> {
mod ops {
use super::*;

#[derive(Clone, Copy, PartialEq, Eq)]
enum OpType {
Deterministic,
Nondeterministic,
}

fn as_list<'gc, C: CustomTypes<S>, S: System<C>>(v: &Value<'gc, C, S>) -> Option<Gc<'gc, RefLock<VecDeque<Value<'gc, C, S>>>>> {
v.as_list().ok()
}
Expand All @@ -1433,13 +1439,9 @@ mod ops {
pub(super) fn prep_index_set<'gc, C: CustomTypes<S>, S: System<C>>(index: &Value<'gc, C, S>, len: usize) -> Result<BTreeSet<usize>, ErrorCause<C, S>> {
fn set_impl<'gc, C: CustomTypes<S>, S: System<C>>(index: &Value<'gc, C, S>, len: usize, dest: &mut BTreeSet<usize>, cache: &mut BTreeSet<Identity<'gc, C, S>>) -> Result<(), ErrorCause<C, S>> {
match index {
Value::List(values) => {
let key = index.identity();
if cache.insert(key) {
for value in values.borrow().iter() {
set_impl(value, len, dest, cache)?;
}
cache.remove(&key);
Value::List(values) => if cache.insert(index.identity()) {
for value in values.borrow().iter() {
set_impl(value, len, dest, cache)?;
}
}
_ => {
Expand All @@ -1449,9 +1451,7 @@ mod ops {
Ok(())
}
let mut res = Default::default();
let mut cache = Default::default();
set_impl(index, len, &mut res, &mut cache)?;
debug_assert_eq!(cache.len(), 0);
set_impl(index, len, &mut res, &mut Default::default())?;
Ok(res)
}

Expand Down Expand Up @@ -1752,7 +1752,7 @@ mod ops {
}
}

fn unary_op_impl<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, x: &Value<'gc, C, S>, cache: &mut BTreeMap<Identity<'gc, C, S>, Value<'gc, C, S>>, scalar_op: &dyn Fn(&Mutation<'gc>, &S, &Value<'gc, C, S>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
fn unary_op_impl<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, x: &Value<'gc, C, S>, cache: &mut BTreeMap<Identity<'gc, C, S>, Value<'gc, C, S>>, op_type: OpType, scalar_op: &dyn Fn(&Mutation<'gc>, &S, &Value<'gc, C, S>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
let cache_key = x.identity();
Ok(match cache.get(&cache_key) {
Some(x) => x.clone(),
Expand All @@ -1764,9 +1764,12 @@ mod ops {
let res = as_list(&real_res).unwrap();
let mut res = res.borrow_mut(mc);
for x in &*x {
res.push_back(unary_op_impl(mc, system, x, cache, scalar_op)?);
res.push_back(unary_op_impl(mc, system, x, cache, op_type, scalar_op)?);
}
match op_type {
OpType::Deterministic => (),
OpType::Nondeterministic => { cache.remove(&cache_key); }
}
cache.remove(&cache_key);
real_res
}
None => scalar_op(mc, system, x)?,
Expand All @@ -1776,60 +1779,60 @@ mod ops {
pub(super) fn unary_op<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, x: &Value<'gc, C, S>, op: UnaryOp) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
let mut cache = Default::default();
match op {
UnaryOp::ToNumber => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(x.as_number()?.into())),
UnaryOp::Not => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok((!x.as_bool()?).into())),
UnaryOp::Abs => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(x.as_number()?.abs()?.into())),
UnaryOp::Neg => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(x.as_number()?.neg()?.into())),
UnaryOp::Sqrt => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(x.as_number()?.sqrt()?.into())),
UnaryOp::Round => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(x.as_number()?.round()?.into())),
UnaryOp::Floor => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(x.as_number()?.floor()?.into())),
UnaryOp::Ceil => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(x.as_number()?.ceil()?.into())),
UnaryOp::Sin => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(Number::new(libm::sin(x.as_number()?.get().to_radians()))?.into())),
UnaryOp::Cos => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(Number::new(libm::cos(x.as_number()?.get().to_radians()))?.into())),
UnaryOp::Tan => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(Number::new(libm::tan(x.as_number()?.get().to_radians()))?.into())),
UnaryOp::Asin => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(Number::new(libm::asin(x.as_number()?.get()).to_degrees())?.into())),
UnaryOp::Acos => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(Number::new(libm::acos(x.as_number()?.get()).to_degrees())?.into())),
UnaryOp::Atan => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(Number::new(libm::atan(x.as_number()?.get()).to_degrees())?.into())),
UnaryOp::StrLen => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| Ok(Number::new(x.as_string()?.chars().count() as f64)?.into())),

UnaryOp::StrGetLast => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| match x.as_string()?.chars().next_back() {
UnaryOp::ToNumber => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.into())),
UnaryOp::Not => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok((!x.as_bool()?).into())),
UnaryOp::Abs => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.abs()?.into())),
UnaryOp::Neg => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.neg()?.into())),
UnaryOp::Sqrt => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.sqrt()?.into())),
UnaryOp::Round => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.round()?.into())),
UnaryOp::Floor => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.floor()?.into())),
UnaryOp::Ceil => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(x.as_number()?.ceil()?.into())),
UnaryOp::Sin => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::sin(x.as_number()?.get().to_radians()))?.into())),
UnaryOp::Cos => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::cos(x.as_number()?.get().to_radians()))?.into())),
UnaryOp::Tan => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::tan(x.as_number()?.get().to_radians()))?.into())),
UnaryOp::Asin => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::asin(x.as_number()?.get()).to_degrees())?.into())),
UnaryOp::Acos => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::acos(x.as_number()?.get()).to_degrees())?.into())),
UnaryOp::Atan => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(libm::atan(x.as_number()?.get()).to_degrees())?.into())),
UnaryOp::StrLen => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| Ok(Number::new(x.as_string()?.chars().count() as f64)?.into())),

UnaryOp::StrGetLast => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| match x.as_string()?.chars().next_back() {
Some(ch) => Ok(Rc::new(ch.to_string()).into()),
None => Err(ErrorCause::IndexOutOfBounds { index: 1, len: 0 }),
}),
UnaryOp::StrGetRandom => unary_op_impl(mc, system, x, &mut cache, &|_, system, x| {
UnaryOp::StrGetRandom => unary_op_impl(mc, system, x, &mut cache, OpType::Nondeterministic, &|_, system, x| {
let x = x.as_string()?;
let i = prep_rand_index(system, x.chars().count())?;
Ok(Rc::new(x.chars().nth(i).unwrap().to_string()).into())
}),

UnaryOp::SplitLetter => unary_op_impl(mc, system, x, &mut cache, &|mc, _, x| {
UnaryOp::SplitLetter => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
Ok(Gc::new(mc, RefLock::new(x.as_string()?.chars().map(|x| Rc::new(x.to_string()).into()).collect::<VecDeque<_>>())).into())
}),
UnaryOp::SplitWord => unary_op_impl(mc, system, x, &mut cache, &|mc, _, x| {
UnaryOp::SplitWord => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
Ok(Gc::new(mc, RefLock::new(x.as_string()?.split_whitespace().map(|x| Rc::new(x.to_owned()).into()).collect::<VecDeque<_>>())).into())
}),
UnaryOp::SplitTab => unary_op_impl(mc, system, x, &mut cache, &|mc, _, x| {
UnaryOp::SplitTab => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
Ok(Gc::new(mc, RefLock::new(x.as_string()?.split('\t').map(|x| Rc::new(x.to_owned()).into()).collect::<VecDeque<_>>())).into())
}),
UnaryOp::SplitCR => unary_op_impl(mc, system, x, &mut cache, &|mc, _, x| {
UnaryOp::SplitCR => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
Ok(Gc::new(mc, RefLock::new(x.as_string()?.split('\r').map(|x| Rc::new(x.to_owned()).into()).collect::<VecDeque<_>>())).into())
}),
UnaryOp::SplitLF => unary_op_impl(mc, system, x, &mut cache, &|mc, _, x| {
UnaryOp::SplitLF => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
Ok(Gc::new(mc, RefLock::new(x.as_string()?.lines().map(|x| Rc::new(x.to_owned()).into()).collect::<VecDeque<_>>())).into())
}),
UnaryOp::SplitCsv => unary_op_impl(mc, system, x, &mut cache, &|mc, _, x| {
UnaryOp::SplitCsv => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
let value = from_csv(mc, x.as_string()?.as_ref())?;
Ok(Gc::new(mc, RefLock::new(value)).into())
}),
UnaryOp::SplitJson => unary_op_impl(mc, system, x, &mut cache, &|mc, _, x| {
UnaryOp::SplitJson => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
let value = x.as_string()?;
match parse_json::<Json>(&value) {
Ok(json) => Ok(Value::from_simple(mc, SimpleValue::from_json(json)?)),
Err(_) => Err(ErrorCause::NotJson { value: value.into_owned() }),
}
}),

UnaryOp::UnicodeToChar => unary_op_impl(mc, system, x, &mut cache, &|_, _, x| {
UnaryOp::UnicodeToChar => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|_, _, x| {
let fnum = x.as_number()?.get();
if fnum < 0.0 || fnum > u32::MAX as f64 { return Err(ErrorCause::InvalidUnicode { value: fnum }) }
let num = fnum as u32;
Expand All @@ -1839,7 +1842,7 @@ mod ops {
None => Err(ErrorCause::InvalidUnicode { value: fnum }),
}
}),
UnaryOp::CharToUnicode => unary_op_impl(mc, system, x, &mut cache, &|mc, _, x| {
UnaryOp::CharToUnicode => unary_op_impl(mc, system, x, &mut cache, OpType::Deterministic, &|mc, _, x| {
let src = x.as_string()?;
let values: VecDeque<_> = src.chars().map(|ch| Ok(Number::new(ch as u32 as f64)?.into())).collect::<Result<_, NumberError>>()?;
Ok(match values.len() {
Expand All @@ -1852,7 +1855,7 @@ mod ops {
pub(super) fn index_list<'gc, C: CustomTypes<S>, S: System<C>>(mc: &Mutation<'gc>, system: &S, list: &Value<'gc, C, S>, index: &Value<'gc, C, S>) -> Result<Value<'gc, C, S>, ErrorCause<C, S>> {
let list = list.as_list()?;
let list = list.borrow();
unary_op_impl(mc, system, index, &mut Default::default(), &|_, _, x| Ok(list[prep_index(x, list.len())?].clone()))
unary_op_impl(mc, system, index, &mut Default::default(), OpType::Deterministic, &|_, _, x| Ok(list[prep_index(x, list.len())?].clone()))
}

fn cmp_impl<'gc, C: CustomTypes<S>, S: System<C>>(a: &Value<'gc, C, S>, b: &Value<'gc, C, S>, cache: &mut BTreeMap<(Identity<'gc, C, S>, Identity<'gc, C, S>), Option<Option<Ordering>>>) -> Result<Option<Ordering>, ErrorCause<C, S>> {
Expand Down
1 change: 1 addition & 0 deletions src/test/blocks/preserve-tensor-topology.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<blocks app="NetsBlox 2.2.0, http://netsblox.org" version="2.2.0"><block-definition collabId="item_-1_2" s="main" type="reporter" category="custom"><header></header><code></code><translations></translations><inputs></inputs><script><block collabId="item_0" s="doDeclareVariables"><list><l>res</l><l>a</l><l>c</l></list></block><block collabId="item_3" s="doSetVar"><l>a</l><block collabId="item_5" s="reportNewList"><list><l>5</l><l>3</l><l>4</l></list></block></block><block collabId="item_11" s="doSetVar"><l>a</l><block collabId="item_13" s="reportNewList"><list><block collabId="item_14" var="a"/><block collabId="item_15" var="a"/><block collabId="item_83" s="reportNewList"><block collabId="item_84" var="a"/></block></list></block></block><block collabId="item_17" s="doSetVar"><l>res</l><block collabId="item_17_1" s="reportNewList"><list></list></block></block><custom-block collabId="item_93" s="check %l into %l"><block collabId="item_93_3" var="a"/><block collabId="item_93_2" var="res"/></custom-block><custom-block collabId="item_66" s="check %l into %l"><block collabId="item_32" s="reportVariadicSum"><list><block collabId="item_34" var="a"/><l>1</l></list></block><block collabId="item_68" var="res"/></custom-block><custom-block collabId="item_70" s="check %l into %l"><block collabId="item_70_1" s="reportVariadicProduct"><list><block collabId="item_70_3" var="a"/><l>2</l></list></block><block collabId="item_70_2" var="res"/></custom-block><custom-block collabId="item_73" s="check %l into %l"><block collabId="item_94_4" s="reportMonadic"><l><option>neg</option></l><block collabId="item_94_6" var="a"/></block><block collabId="item_94_5" var="res"/></custom-block><custom-block collabId="item_78" s="check %l into %l"><block collabId="item_78_1" s="reportMonadic"><l><option>2^</option></l><block collabId="item_78_3" var="a"/></block><block collabId="item_78_2" var="res"/></custom-block><block collabId="item_80" s="doReport"><block collabId="item_81" var="res"/></block></script></block-definition><block-definition collabId="item_49" s="check %&apos;val&apos; into %&apos;res&apos;" type="command" category="custom"><header></header><code></code><translations></translations><inputs><input type="%l"></input><input type="%l"></input></inputs><script><block collabId="item_35" s="doAddToList"><block collabId="item_54" s="reportNewList"><list><block collabId="item_59" var="val"/><block collabId="item_54_2" s="reportIsIdentical"><block collabId="item_54_3" s="reportListItem"><l>1</l><block collabId="item_63" var="val"/></block><block collabId="item_54_4" s="reportListItem"><l>2</l><block collabId="item_64" var="val"/></block></block><block collabId="item_86" s="reportIsIdentical"><block collabId="item_86_1" s="reportListItem"><l>1</l><block collabId="item_86_4" var="val"/></block><block collabId="item_86_2" s="reportListItem"><l>3</l><block collabId="item_86_3" var="val"/></block></block><block collabId="item_89" s="reportIsIdentical"><block collabId="item_89_1" s="reportListItem"><l>2</l><block collabId="item_89_4" var="val"/></block><block collabId="item_89_2" s="reportListItem"><l>3</l><block collabId="item_89_3" var="val"/></block></block></list></block><block collabId="item_36" var="res"/></block></script></block-definition></blocks>
22 changes: 22 additions & 0 deletions src/test/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,28 @@ fn test_proc_variadic_sum_product() {
});
}

#[test]
fn test_proc_preserve_tensor_topology() {
let system = Rc::new(StdSystem::new_sync(BASE_URL.to_owned(), None, Config::default(), Arc::new(Clock::new(UtcOffset::UTC, None))));
let (mut env, _) = get_running_proc(&format!(include_str!("templates/generic-static.xml"),
globals = "",
fields = "",
funcs = include_str!("blocks/preserve-tensor-topology.xml"),
methods = "",
), Settings::default(), system, |_| SymbolTable::default());

run_till_term(&mut env, |mc, _, res| {
let expect = Value::from_simple(mc, SimpleValue::from_json(json!([
[[["5", "3", "4"], ["5", "3", "4"], ["5", "3", "4"]], true, false, false],
[[[6, 4, 5], [6, 4, 5], [6, 4, 5]], true, false, false],
[[[10, 6, 8], [10, 6, 8], [10, 6, 8]], true, false, false],
[[[-5, -3, -4], [-5, -3, -4], [-5, -3, -4]], true, false, false],
[[[32, 8, 16], [32, 8, 16], [32, 8, 16]], true, false, false],
])).unwrap());
assert_values_eq(&res.unwrap().0, &expect, 1e-5, "preserve tensor topology");
});
}

#[test]
fn test_proc_variadic_min_max() {
let system = Rc::new(StdSystem::new_sync(BASE_URL.to_owned(), None, Config::default(), Arc::new(Clock::new(UtcOffset::UTC, None))));
Expand Down

0 comments on commit 8f494ab

Please sign in to comment.