Mean (Spectral) NILL

When I put a NILL in the Mean (Spectral), the output is 0.0000 but shouldn’t that be a NILL as well?

Now I have to do the Avoid Nill stuff BEFORE the mean node.

Mean NILL.v4p (2.3 kB)

helo René,

well. this is actually by design.
of course it would be very interesting why it is getting in your way exactly.

it seems that you expect the “NIL info” somehow get propagated through the graph to somewhere pretty much downstream where you just can react to that exceptional behavior once(?) and somehow the mean node gets in you way?

it is a bit hard to follow why exactly your intuition doesn’t match vvvv’s behavior here. but i am pretty sure that if we find the problem we can learn much out of it. maybe also the avoidnil needs to be binsizable (avoiding bins of size 0) or stuff like that. but let’s wait for your patch that’s a bit more complex and shows the undesired behavior.

why mean behaves like it does

mean (spectral), just like + (spectral) belongs to the kind of spread operations, that consume a spread and output one value. i know you knew - anyway, let’s just roughly separate three categories of different spreading behaviors:

(ignoring binsizes for now)

*spread generators (like i) 1->n
*normal nodes n->n
*spread consumers (spectral nodes) n->1

when just focussing on the central functionality of a node (ignoring all spreading alltogether) then normal nodes really don’t do anything with spreads. they are no spread ops (take a damper).
now getting spreading back in, the basic concept with spreading was to just repeat this basic functionality for all slices.
that way you get NIL for a NIL input…

the other two sets of nodes either consume or produce spreads. and this is their core functionality! it is the core functionality of a linearspread to take one value and produce a spread and the core functionality of a mean to take a spread and produce one value.

now of course people wanted again be able to express “do the core functionality over and over again”.

we introduces binsizes and by that you had both:

  • a spread op generating (1->m) or consimung spreads (m->1)
  • the again valid basic spreading mechanism of “do something over and over for all slices” (n->n), wrapping the basic spread operation in a simple loop again and by that (n->n ms) or (n ms-> n)

what does it all mean? - i already know all of that!

yeah. well putting it together and talking about the mean node: “do mean over and over for all input slices” now refers to the several subspreads (or bins) that are handed over to the core “mean” functionality…

in your case this would mean that in order to get NIL you would need to have NIL spreads as an input.
now how would you do that? yep. NIL binsizes would mean that you have no bins, right?
hard nut. so outer spread count or “n” is controlled by the spreadcount of the binsize pin… i know this is so easy to grasp and have in mind, one would love to cry…

so there you have it. you can output NIL with that node. but i don’t know if that helps you too much??

by the way vvvv only invented the “loop over all concept with some modulo magic”.

for the coders or theory lovers: stuff like spread ops are very much existent outside vvvv as well. in the functional world e.g. spread generators are named (or based on) unfold and spread consumers are based on fold… google Erik Meijer, Linq or “Bananas, Lenses, Envelopes and Barbed Wire” to see where you get when you start with fold and unfold. :D

It’s always a pleasure to read your posts, gregsn. No irony involved here.

Wow, thanks Gregsn, great read.

Now I just see and realize it is with all the spectral nodes.

My feeling just says, if I put nothing in, I will get nothing out, and not a zero. Why not a 42? But a 0.0000?

Not really important anymore (motivation for this question):

I was doing a mean (spectral) on some integers, and that could result in a 0.000, so in the end I had no way to know if I had produced a NIL or that the output really was 0.000

I worked around the issue and even came up with a much much cleaner solution, so all is good on this side.

perhaps a binsize output could make it easier to distinguish between a spread with a mean of 0 and an empty spread.

@42 and binsize out: you both are totally right.

mean is special. i didn’t see that at first.
mean internally ends with a division by the count, which in case of NIL results in an invalid divsion by zero.

so what we would need to think about here is what we would like the normal / (Value) node to behave like.
when we know that we know what the mean should behave like.

up to know / silently catches the exception and moves on. (resulting in setting no new output, but just keeping the old, which probably is the worst implementation you could think of) on the other hand nobody complianed about that all these years, did they?

anyway solutions (that are independent of former frames) include:

  • a) new input called “Default Output on Error” defaulting to 0
  • b) new output called “Success” (bool) - a spread of zeros and ones
  • c) new output called “BinSize” (int) - again a spread of zeros and ones, this time however reducing the amount of values on the original output.

(1) / (0, 10, 100)

a) output: (0, 1/10, 1/100)
b) output: (x, 1/10, 1/100), with x being NaN, last output, 0 or 42;
success: (0, 1, 1)
c) output: (1/10, 1/100);
binsize: (0, 1, 1)

sunep i think your solution c is nice. on the other hand think about all the places where you had a divide and now suddenly can’t be sure about getting n outputs when feeding n inputs (when just focussing on the original output pin).
of course the best way to interpret output+binsize is this:
(NIL, (1/10), (1/100))
so that IS 3 values, but only if you really feed that binsize correctly to all the consuming downstream nodes.

the strange thing about that solution is that a divide suddenly acts like a spread producer, adding one dimension of complexity to your patch. which doesn’t feel right.

so maybe we need a combination of a) and b) being able to specify the fallback output value together with the information if division was successful.

(that way you could at least filter out the nasty fallback values (using select) and also interpret the success pin as a binsize for the resulting filtered spread.)

so if we have a solution for / we can apply that to mean. what do you think?

of course the standard solution in other languages is

d) embrace NaN
or even
e) throw exception, and catch it somewhere else (e.g. in the surrounding patch). let me just say that this option is not an option :D

After thinking a bit, I would think that solution b) NaN in combination with a success pin would be the best.
I agree that binsize is a bit weird, since it would only have either 0 or 1 as a value.

Hello @gregsn, from student to teacher: isn’t Ø a more articulated concept than NaN, and isn’t NaN already, at least conceptually, "a “part of” Ø?

Can you explain me advantages we could have from adding an Output Bin Size? The only change I can think of (in vvvv45) is that you may patch some logic after Mean too, instead than only before it.

Is NaN a concept we will deal with in vvvv50 and NIL is going to disappear? @joreg said that spreads will no longer be what they are now, that’s why I ask this.

hey h99,

Sorry for the late reply. there are too many ways to reply - so here is my random answer…

First let me tell you that from a user perspective i am not a proponent of NaN. NaN is a pretty harsh but correct way to communicate that vvvv was asked to do something that is impossible - x/0 is just not a number.

A user never should ask vvvv to do that. a switch underneath covering the special condition should take care of the special case; thus never leading to the evaluation of the division by zero…

An implementation that embrazes NaN would communicate that error in a very visible way, by propagating NaN downstream and pretty much “disabling” all functionality that crosses its way.

While developing you will love this feature, since you will see immediatly that something is broken. In any other case you will not be amused… So i have my concerns with NaN regarding its compatibility with our rather playful toolkit.

I guess my favorite is a combination of a) and b) where you specify a fallback output value for the not evaluable invalid operation and an output telling you so (bool).

I am not a proponent of that solution, but anyway.

Adding an output bin size is like saying your original output is a spread of spreads (of numbers).

So the binsize pin serves as a helper to tell you more about another pins’ values. having it as an output also often means that you want to connect it to some binsize input to communicate the spread structure to everything that comes downwards.

If you don’t do that you might loose valuable information.

the / node example:

  • in all cases where you connect the binsize output further downstream you should be fine. the question is if there are good examples where you would really see this fit and really want to connect the rather strange binsize output to an input.
  • in all cases where you don’t connect that binsize you just transmit the leaves - the numbers - of your data structure, which actually is a spread of: either a number or an empty spread. you might put 8 values into the node and get 6 output values + 8 binsizes (two of them 0), which you just ignore. this is dramatic. since vvvv is very flexiblle with interpreting your data, your next node downstream that expected 8 input values will just run fine (but randomly wrong) with 6 input values.

What i am trying to say is: in vvvv you subconsciously work with a strong estimation in mind: the spreads on the several input pins of your node shall match. the example above would break that intuition at a place where you wouldn’t expect.

Typing of data is about what to expect.
The question of yours is about typing.

NaN can be seen as part of the real number set. i am not too much into math to really tell you if this is a hack, but that’s not the point.
if i know that i can expect from a single division to output a single value, then this is something completely different than knowing that i can expect a spread of values (containing one or zero values).

In the current system you often feed spreads of the same length to several input pins of a node. then (with the standard nodes) you get exactly that number of slices on the outputs. you move on and feed them to another node and there again slices of several inputs get matched and fed to some core functionality.

So often enough you have a cluster of nodes that each will run its core functionality exactly n times, as the node downstream and the node nearby.

In vvvv50 you will work more slice oriented. you will have an area where you define what to do for one slice and then feed spreads to the whole region. by that you will have the certainty that slices match.

Spreads will still exist, but we might name them otherwise.

What will disapear is the magic implicit looping. a damper e.g. just has nothing to do with spreads, so it won’t magically work with them. other nodes that operate on spreads (aka collections) will however exist obviously.

NaN hasn’t been discussed really.

hello @gregsn, thank you for your reply.
From user to devvvv, the great thing about vvvv is that it’s always “running”, we cannot generate an exception with our patches, and that we have data [value, string, color, …](value, string, color, …) and Ø.
I mean, two simple scenarios: if I have data I do something with it; if I have Ø I do something else. Very simple to manage.
Adding a NaN scenario, would just make things more complicated. I don’t care if something is NaN: in a visual stream of nodes I can immediately understand what is going wrong and where this happens, and do something.

This is something I didn’t know how to explain (maybe I didn’t understand it clearly, until now).

That’s what I meant when I said one will only be able to patch some more logic after Mean node too, instead than only before.

Please, keep vvvv playful ;)

TY again

Sometimes I feel pretty Mean

you mean it?

i added a success pin to several nodes.

division and mod node:

  • success as long as second input is not 0
  • if success the output gets written to, otherwise nothing happens

some other spectral nodes (mean, variance, …):

  • success for all bins with more than 0 slices
  • all bins get calculated, no matter if one in between “fails”
  • for each bin you get a success slice
  • for each bin the result is written to the output if the bin calculation didn’t fail
  • thus again last valid value sticks

that way the result doesn’t jump in the corner cases.
no legacy nodes needed.
you can use the success pin to filter out old results with a select node.