Skip to content
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

subLevel support in several nodes #477

Closed
nortikin opened this issue Nov 15, 2014 · 32 comments
Closed

subLevel support in several nodes #477

nortikin opened this issue Nov 15, 2014 · 32 comments

Comments

@nortikin
Copy link
Owner

i use this layout to show how paradigm works.
masking
https://yadi.sk/d/KS0t3boMcjMcs
scaling
https://yadi.sk/d/XvHbRIO1cjMcy

in masking grouped in frame transformation nodes, that not support sublevels of data. i must confess, non of this not use, + vector in and out not support. More i want to do - transformation nodes has to output subleveled data. it becomes two ways complicated thing. if we take scale node, i made separator flag to make sublevel for UVconnect, but if we input separated grouped vertices, how to be? what we should output than? and how to group? input and output has to support in at least two levels.

Sencerely, UVconnect node supports only two cases - usual levels and grouped levels. So, I will make scale node as experimental to deal inputs grouped and ungrouped and output both cases 2x2=4 cases

@nortikin
Copy link
Owner Author

to deal with manylevels we need recursion in every node, in uvconnect is complicated, i should rethink it, because we deal in combinations of data there, so, if we input nested data, and work with last level, to save hierarchy it is almost impossible, as i see. But how to combine different levels of data if there is more than two? what connect to what? It is strongly unusable for everage user to think everytime about how many levels i have? and other things. So, @ly29 told right - we not need many levels, maybe additionaly one more? not more than two additional levels anyway.

@zeffii
Copy link
Collaborator

zeffii commented Nov 15, 2014

I still think it's interesting to consider a flat array (nparray) instead of explicit levels. The flat array data should include a reshape scheme indicating the format of the most atomic data, and upwards. As most nested lists are probably uniform in length, so list might be [[10],[10],[10]] (sublists are ten items long).

If you have an array of vectors of xyz (or xyzw= more general) and you have 10 vectors per nested level and three of such lists per datastream. you would reshape so

flatarray => reshape (3, 10, 4) => 

then the datatype could be

dict(flatarray=[120 floats], reshape_scheme=(3, 10, 4))

ofcourse, this breaks down if not each sublist has 10 vectors...

@ly29
Copy link
Collaborator

ly29 commented Nov 15, 2014

I think we do need levels. That is what I have been saying a long time. What we do need is structure that is easy to parse.
Recursion does not belong in individual nodes but in the system.
Som nodes like uv connect might have to do it but most nodes don't.
List tuple for structure, n.array or others like str and boy.types.Object for the actual data.

@nortikin
Copy link
Owner Author

One more
nestyness not needed for object socket. Is it?

@zeffii
Copy link
Collaborator

zeffii commented Nov 15, 2014

Information about the structure of the data is known when sent to an output, so it's only logical that it can be included in that datastream, allowing the receiving node to parse with higher confidence.

@nortikin
Copy link
Owner Author

@zeffii i have to investigate numpy, because what i allready know stops me from using it for Sverchok. Will be familiar, than canmake conclusions of how to use. Maybe you right to use it.

@ly29
Copy link
Collaborator

ly29 commented Dec 9, 2014

Okay the right answer to this if make it happen, the sooner the better.
If not numpy we can define our own classes. It is not that hard as long as we make a structure that is easy to parse, which is a better idea than make a separate structure storing the same info.

@nortikin nortikin mentioned this issue Oct 5, 2016
47 tasks
@alessandro-zomparelli
Copy link
Collaborator

Hi everybody, I'm writing in this conversation because to me seems the most correct one.
I have good experience in Grasshopper+Scripting and I think that how grasshopper manage the data is much more correct and stable, adn maybe we should consider to take inspiration from that.

Basically when you script a node, you have to declare if a specific socket should receive single data (with the possibility to specify the type), a list (type as well) or a DataTree. Then accordingly to that all the matches are processed from the component.
I think that instead to make all the matches inside each node, this part of code should be external, in order to prevent many duplicate codes.
I think that this approach should make Sverchok much more robust allowing to combine different data structures without incurring in annoing errors. Here you can see how Grasshopper can combine different type of structure:
immagine

Here is my question: do you think that it is possible to create a general code that take care of that making the code inside each component more light and stable?

I know that the side effect of this is the computation time needed, but I also think that stability is more important than performance... What do you think? Maybe we can try making some nodes that works with this paradigm in order to test it...

Cheers

@alessandro-zomparelli
Copy link
Collaborator

I made a scheme in order to describe the idea:

001 - data matcher

In this case I'm processing the data using "Longest List" strategy. It means that when two list doesn't have the same length, then the last element of the shortest list is used until all pairs are matched.

I'm also considering a node that combine single elements. For example a component Addition in this case is simply coded as:

a = input socket float
b = input socket float
return a+b

No lists, no nested levels, all the complicated stuff is managed by the Data Matcher.
Does it make sense? I think that this is how Grasshopper works... I don't really know if it is possible to do something similar in Sverchok.

@vicdoval
Copy link
Collaborator

Hi @alessandro-zomparelli right know the data matching is done calling functions .data_structure or .sv_itretools, so, being true that they happen inside the node the matching functions are not written in the node's code.
If you check the scalar_mk2.py (Math MK2) you will see all the data matching is done by the recurse_fxy function, it seems something as the datamatcher you propose. (It also uses the longest list strategy but i guess that could be a parameter)

I don't know if it could fit all the nodes but it could be a starting point to a resolve(data, function) paradigm

I m not sure if I understood your thought/proposal and its benefits, for me as coder the data match is solved usually with a couple of calls to list_match functions but i guess it could be done simpler or more consistent
nodetrees

@nortikin
Copy link
Owner Author

You right. It have to be smarter. It is happened due to my weak skills in programming. If we can make more automatisation it is always great.
My personal attempt was to manage transformation nodes (move and scale at the moment). Not even sure i doing right

@nortikin nortikin reopened this Oct 14, 2018
@alessandro-zomparelli
Copy link
Collaborator

Hi @vicdoval , yes it actually solves a lot of problems. I didn't know that, thanks!
So, if I understand correctly you have to create a function do_something, and then you can call recurse_fxy(x, y, do_something) in order to get your result right? And it should work with any kind of data, right?

If I'm right in this function we still have some limitations (in order of importance), tell me if I'm wrong, I've checked it quickly:
a) It works with only two data sets x and y
b) it handle only elements matches, not lists (example: if x should be a list while y is a single element)
c) It breaks vertices, because tuples are treated as lists, instead of single element
d) It works only with constant number of levels, for instance it can't handle [[0,1,2], 3] (this is a nice to have, but it's not the most important point)
e) Best scenario would be to have this stuff happend in background without calling functions, but it's still not too bad, the most important thing is the stability. Hiding those function will make probably easier to contribute to Sverchok without risks.

@nortikin don't be so hard on yourself! :-D

@Durman
Copy link
Collaborator

Durman commented Oct 14, 2018

@alessandro-zomparelli There is list match node also. It gives possibility to make lists with equal length.

lsist match

@alessandro-zomparelli
Copy link
Collaborator

@Durman I see. This is quite useful, but what I'm thinking is something similar to that, running in background in every node in order to manage just once all irregular situations.
I'm trying to arrange a prototype in order to make it more clear.
The passages that I've imagined are:

  1. Take every kind of wierd data structure and convert to one list of lists. This is necessary in order to match them (like in the node, that so far can't handle all possible structures)
  2. Doing 1 I need to store path information in order to rebuild the correct structure later. This information should allow to reintroduce the correct amount of levels.
  3. Match
  4. Function on paired elements
  5. Rebuild the data structure with the correct number of levels.

Things are a bit more complicated if one node should compute lists on one side, and single element on the other side.

Please, tell me if you see some advantages in that process... I don't want to overcomplicate things, but I've experienced some frustration with some node that doesn't process correctly the given data because the number of level is different than expected.
Do you guys have used Grasshopper before?

@vicdoval
Copy link
Collaborator

The recurse_fxy(x, y, do_something) is not implemented across all the nodes and even in the same node there is another call to recurse_fx used when there is just one parameter. The limitations you mention are all true and i guess that function was written to be used in the Math node for all the math functions :)

I imagine there could be a function inspired in this ones like recurse_f([lists], [final_levels], do_something)
that could expand the functionality to other cases

The problem i see is that sometimes is much more effective to process part of the data on a higher level. In this cases using this handy function would lead to an excess of computation if used as a default behaviour

By this moment Sverchok nodes usually works with two levels of nesting: [list] and [list of lists] (excluding the vector level which would be another level) for higher hierarchies I've been using the Monad vectorization. Some nodes may not take the [list of lists] level but they can be encapsulated in a vectorize Monad for that case. Is true I experienced some frustration at the begining with the nesting thing but i got use to it 😄
Later, as coder I use happily the data_structure.py functions to match the lists
I haven't used Grasshopper for very long, but i been studding many design strategies implemented in GH and from my perspective Sverchok has develop its own flavour, of course I would love to have NURBS and many other Gh goodies, but i find Sv to be really flexible and easily expandable. (and obviously open source in open source)
Also I think the Sv strategy leads to have less nodes with more modes and Gh tends to have more nodes with less modes. This variety off modes in many of the nodes I think goes against the possible uniformation of the data_matching.

Any way the open source nature is meant to evolve and improve and although I have learned a lot studding the Sv nodes and under my view (architectural background) they where usually pretty amazing and something to mimic, I'm sure there is always space for improvements.

So if you create a cool implementation it will probably be used by every future developer😃

@alessandro-zomparelli
Copy link
Collaborator

Thank you very much @vicdoval for your answer!

The recurse_fxy(x, y, do_something) is not implemented across all the nodes and even in the same node there is another call to recurse_fx used when there is just one parameter. The limitations you mention are all true and i guess that function was written to be used in the Math node for all the math functions :)

I imagine there could be a function inspired in this ones like recurse_f([lists], [final_levels], do_something)
that could expand the functionality to other cases

The problem i see is that sometimes is much more effective to process part of the data on a higher level. In this cases using this handy function would lead to an excess of computation if used as a default behaviour

By this moment Sverchok nodes usually works with two levels of nesting: [list] and [list of lists] (excluding the vector level which would be another level) for higher hierarchies I've been using the Monad vectorization. Some nodes may not take the [list of lists] level but they can be encapsulated in a vectorize Monad for that case. Is true I experienced some frustration at the begining with the nesting thing but i got use to it 😄

I see. Of course we can get used to that, but as teacher, I would like to use Sverchok as tool in some workshops, and for the new users some nodes can generate some frustration... :-/

Later, as coder I use happily the data_structure.py functions to match the lists

Oh, I didn't seen that functions. I really struggled in order to make the Gcode Exporter able to process any kind of structures. I added some functions here: https://github.com/alessandro-zomparelli/sverchok/blob/gcode/utils/sv_itertools.py
Do you see duplicates? Do you think that I should move them in data_structure.py?

I haven't used Grasshopper for very long, but i been studding many design strategies implemented in GH and from my perspective Sverchok has develop its own flavour, of course I would love to have NURBS and many other Gh goodies, but i find Sv to be really flexible and easily expandable. (and obviously open source in open source)

I don't mind too much the NURBS stuff. Also in GH when you want to do complex things you need to use meshes. I like how SV is able to collaborate with modifiers and other Blender functions.

Also I think the Sv strategy leads to have less nodes with more modes and Gh tends to have more nodes with less modes. This variety off modes in many of the nodes I think goes against the possible uniformation of the data_matching.

I see what you mean. Differently than GH users, SV are more likely Graphic Artists that doesn't always have strong background in math. Maybe an approach with packed "effects" is more easy to understand.
Regarding the variety of modes, I think that it shouldn't be a real issue. I did some work with Gcode Exporter, and after a huge headhache I was able to manage really complex situations (I think :P).
The issue as I predicted is an increase in computing time... :-/ Do you think that is possible make it lighter in some way?

Any way the open source nature is meant to evolve and improve and although I have learned a lot studding the Sv nodes and under my view (architectural background) they where usually pretty amazing and something to mimic, I'm sure there is always space for improvements.

So if you create a cool implementation it will probably be used by every future developer😃

Let's see if I can make something that is not insanely heavy :P So far I learned a lot from the existing code of other SV developers. Python can be really awesome, is really a shame that when things get complicated everythong start to slow down, more than other languages

@Durman
Copy link
Collaborator

Durman commented Oct 16, 2018

@alessandro-zomparelli My implementation your idea in Sverchok. I also had similar ideas before when I had experiments with big structures like a town.))

2018-10-16_08-54-49
https://gist.github.com/Durman/f4c4f8ce465d148a42d9113f0e8cb4eb

We have something close to description of your ideas named mask. Usually mask represents structure of data. It uses for apply effects of a node to part of input data but also it can be used for rebuild data to initial view.
We can use special nodes for join data to list and separating it again for using this technique that will allow to increase speed.

@alessandro-zomparelli
Copy link
Collaborator

alessandro-zomparelli commented Oct 16, 2018

Thanks @Durman , the result seems good! I'll take some time to study the gode.

I was thinking at something that made me feel really stupid. I never really understood why interacting with Data Tree in Grasshopper was more complicated that just working with sublists (that to me seemed more intuitive). I think that I've just realized why :-D :
immagine
I'm really thinking Grasshopper Data Trees are just simple lists of lists. Actually a dictionary of lists.
The depth of the structure maybe is just achieved with string paths, like {0;0;1} that means data[0][0][1].
If you think about it, you just need to sort the elements by keys and you get the correct order of the branches:

`dict = {}
dict['{0;1}'] = list(range(5))
dict['{0;0;1}'] = list(range(4))
dict['{0;2;0;1}'] = list(range(3))
dict['{0;0;0}'] = list(range(3))
dict['{0;0;2}'] = list(range(5))
print(sorted(dict))

['{0;0;0}', '{0;0;1}', '{0;0;2}', '{0;1}', '{0;2;0;1}']`

If you need to find different ways to combine the lists, you just need to edit the keys, instead of digging into several lists for understanding how deep are them!

EDIT: This also simplify a lot the process of reassembly the lists after the node have processed them.

@alessandro-zomparelli
Copy link
Collaborator

alessandro-zomparelli commented Oct 16, 2018

I tried to replicate this situation from Grasshopper:
immagine
in python using this code:

a = [[[0,1,2],[3,4],5,[[6,7]],8],[9]]

def list_pathes(data, dict, path):
    _list = []
    count = 0
    for i in range(len(data)):
        l = data[i]
        if isinstance(l, list):
            list_pathes(l, dict, path + ";" + str(count))
            count+=1
        else:
            _list.append(l)
    if _list != []:
        dict[path + ""] = _list

def list_to_dict(list):
    dict = {}
    list_pathes(list, dict, "0")
    return dict

print(list_to_dict(a))
print(sorted(list_to_dict(a)))

returned this:

{'0;0': [5, 8], '0;0;2;0': [6, 7], '0;0;0': [0, 1, 2], '0;0;1': [3, 4], '0;1': [9]}
['0;0', '0;0;0', '0;0;1', '0;0;2;0', '0;1']

As you can see it takes a wierd data structure and convert it into a dictionary of lists. Sorting them by path give the same order that Grasshopper do and this allow to combine correctly complex lists with others. Because the matching can happen just with paths (a list of strings), this should be much more efficient than managing and modifing the actual list.

What do you think?

@Durman
Copy link
Collaborator

Durman commented Oct 17, 2018

We can do something similar in Sverchok. I don't know in which case this data structure can be useful. Can you give any practical examples?

2018-10-17_09-07-28

@alessandro-zomparelli
Copy link
Collaborator

alessandro-zomparelli commented Oct 17, 2018

What I found really interesting of the Grasshopper's DataTree is that you can combine them also if the structures are completely different:

immagine

As you can see, no matter what's the shape, GH matches them in order using "longest" method. Then inside the lists by default he uses "longest" again.

And in my opinion the advantages are:

  1. No matching errors, I have never seen them in Grasshopper. At the moment in Sverchok some nodes doesn't work if you don'tfeed them with the desired structure.
  2. You can easily manage matches between the branches because the depth is fixed (one dictionary, with simple lists)
  3. The computing cost for doing such operations will be less, because, as I've experienced, instead of digging inside the lists you can just check their keys (path)
  4. This allows to simplify the management of Grasshopper components, that works with simple elements, sometimes simple lists (only in particular cases you use the whole tree), but then reassembly them using the keys, in order to not loose their "virtual structure" (I'm calling it virtual because it's just a dictionary)

If we look at the paths, apparently GH keeps the keys of the first data, adding then a new key if they doesn't match:
immagine
To me it seems more consistent to just use the longhest tree's paths. It looks really wierd, but this is a very unusual condition. I was just stressing his managment of Data and he did the job without any warnings or errors.

@ly29
Copy link
Collaborator

ly29 commented Oct 17, 2018

Well the flatish nature of Sverchok have been up for discussion many times before.

Fixing this would require some rewriting but this could be done in an incremental way bye wrapping older nodes and converting them at a suitable time.

@nortikin
Copy link
Owner Author

@alessandro-zomparelli you right. For some data we need non-orthogonal trees.
Zeffii proposed many times numpy implementation, but there should be None's as placeholders and special workprocess with this placeholders
for list [[1,2], 3, [4]] it should looks like np.array([[1,2],[3,none][4,none]]) with control array np.array(2,2,1,1,2,2) for levels of data.
Personally i not understand clear should it be usefull or not.
Better to manipulate similar level in one container with numpy. And numpy is faster!!! maybe it is more important than GH-alike process.

@alessandro-zomparelli
Copy link
Collaborator

alessandro-zomparelli commented Oct 17, 2018

Fixing this would require some rewriting but this could be done in an incremental way bye wrapping older nodes and converting them at a suitable time.

Yes, I was thinking at making maybe some nodes for testing the idea. Just the basics one.

Better to manipulate similar level in one container with numpy. And numpy is faster!!! maybe it is more important than GH-alike process.

Yes, this is a very good point. Initially I started looking at numpy, and it must be considered in some way, because it's the most powerfull tool in Python for numerical operations. The difficulty that I see, is to keep easy to script nodes that manages those kind of data without giving errors.
Having nodes that gives errors because of the data structure can be really annoying, especially for new users. The reason why I'm looking at GH is just because I wanted to understand how David Rutten solved that issue. If you think that we can just use numpy for that, that's great, my concers are due to my limited knowledge of python and numpy. Actually I found really interesting functions inside the Sverchok code and I learned a lot. Maybe there is a more Pythonish and elegant way.

The prototype that I would like to test, will use flat numpy arrays for managing the individual lists and a dictionary as general container. A kind of hybrid dictionary-numpy.
I'm thinking at a function like that:

# Closest Point
out = {}
for point, pointCloud, key in match_data_trees((tree1, tree2), ('ELEMENT', 'LIST')):
    out[key] = closestPoint(point, pointCloud)
return out

I haven't thought so much about that yet, maybe in that way doesn't make sense, but imagine that you are creating a node that find the closest point inside a point cloud. You don't have to figure which kind of data is arriving to you, just tell which data you want. In this particular case you want to process the point one by one, while you need a list of points representing your cloud.

@Durman
Copy link
Collaborator

Durman commented Oct 29, 2018

I have tried to implement algorithm that makes list of data flattened with links to each value that represent structure of initial data.
Perhaps it can be useful for someone.

def get_flatten(data):
    
    def flatten(data, link=[]):
        try:
            for i, value in enumerate(data):
                yield from flatten(value, link=link + [i])
        except TypeError:
            yield data, link

    gen = flatten(data)    
    return {tuple(link):value for value, link in gen}
>>> get_flatten([[1,2],3])
>>> {(0, 0): 1, (0, 1): 2, (1,): 3}
>>> get_flatten([1,[[2,3],4,5],[6,[[7],[8]]],9])
>>> {(0,): 1, (1, 0, 0): 2, (1, 0, 1): 3, (1, 1): 4, (1, 2): 5, (2, 0): 6, (2, 1, 0, 0): 7, (2, 1, 1, 0): 8, (3,): 9}

@alessandro-zomparelli
Copy link
Collaborator

alessandro-zomparelli commented Nov 1, 2018

Hi @Durman thank you very much. Your code is really neat, I have to study it better...
I was thinking at something that behave more in that way:

a = [[[0,1,2],[3,4],5,[[6,7]],8],[9]]

def list_pathes(data, dict, path):
    _list = []
    count = 0
    for i in range(len(data)):
        l = data[i]
        if isinstance(l, list):
            list_pathes(l, dict, path + ";" + str(count))
            count+=1
        else:
            _list.append(l)
    if _list != []:
        dict[path + ""] = _list

def list_to_dict(list):
    dict = {}
    list_pathes(list, dict, "0")
    return dict

print(list_to_dict(a))

The result is something like this:

{'0;0;1': [3, 4], '0;0;0': [0, 1, 2], '0;0;2;0': [6, 7], '0;0': [5, 8], '0;1': [9]}

As you can see in this case it stores lists instead of the single elements. For sure with your Python skills you can make it more simple/optimized. I'm writing some other code that combine those lists as numpy array, in order to make mathematical operations that works with every kind of data structure (like in that example).

I was also trying to figure how to try to implement this approach in the existing Sverchok ecosystem. Maybe a child class of node with some specific methods that process automatically the dictionaries.
Also two nodes that convert lists to dict and vice versa in order to connect them with all existing nodes.
I would like to test it with some basic nodes in order to see if it is convenient or not...

@alessandro-zomparelli
Copy link
Collaborator

alessandro-zomparelli commented Nov 1, 2018

@Durman I studied your code, and I learned a lot. I'm not really familiar yet with some pythonish strategies... :P
I made this version that generate a dict of lists. Instead of using strings for the path, I used tuples, like you did, that makes much more sense. While instead of using enumerate I decided to use a counter, in order to create a list of all single elements that are in the same level:

def list_to_dict(data):
    
    def list_pathes(data, path=()):
        elements = []
        count = 0
        for branch in data:
            if isinstance(branch, list):
                yield from list_pathes(branch, path + (count,))
                count+=1
            else:
                elements.append(branch)
        if len(elements) > 0: yield elements, path
    
    gen = list_pathes(data)
    return {path:value for value, path in gen}

@alessandro-zomparelli
Copy link
Collaborator

alessandro-zomparelli commented Nov 1, 2018

Well, I added a Numpy part (at the moment is probably a bit ugly). This is the full code:

# DataTree

def list_to_dict(data):
    
    def list_pathes(data, path=()):
        elements = []
        count = 0
        for branch in data:
            if isinstance(branch, list):
                yield from list_pathes(branch, path + (count,))
                count+=1
            else:
                elements.append(branch)
        if len(elements) > 0: yield elements, path
    
    gen = list_pathes(data)
    return {path: value for value, path in gen}

# numpy DataTree 

def tree_to_numpy(tree):
    for k, list in tree.items():
        try: tree[k] = np.array(list)
        except: pass
    return tree

def numpy_longest(arr):
    matched_arr = []
    length = max([a.shape[0] for a in arr])
    for a in arr:
        extend = np.array(([a[-1]]*(length - a.shape[0])))
        matched_arr.append(np.concatenate((a, extend)))
    return matched_arr

def match_trees(trees, mode='LONGEST'):
    out_trees = []
    if mode == 'LONGEST':
        max_length = max([len(t) for t in trees])
        for t in trees:
            lists = [t[key] for key in sorted(t.keys())]
            length = len(t)
            out_trees.append(lists + [lists[-1]]*(max_length-length))
            if length == max_length:
                keys = list(sorted(t.keys()))
    for i in range(max_length):
        matches = numpy_longest([t[i] for t in out_trees])
        for tree, matched_tree in zip(out_trees, matches):
            tree[i] = matched_tree          
    out_trees.append(keys)
    return out_trees

I executed this, as test:

a = [[[0,1,2],[3,4],5,[[6,7]],8],[9]]
b = [[0,10,20],[30,40,60,70],80,90]

# Convert generic data to numpy, when possible
tree_a = tree_to_numpy(list_to_dict(a))
tree_b = tree_to_numpy(list_to_dict(b))
matched_trees = match_trees((tree_a,tree_b))

def myFunction(list_1,list_2):
    return list_1 + list_2

result = {}
for list_a, list_b, path in zip(matched_trees[0], matched_trees[1], matched_trees[2]):
    result[path] = myFunction(list_a, list_b)
    
print("\n" + str(result))
print("Result - Lists: " + str([result[key] for key in sorted(result.keys())]))

That prints:

{(0, 1): array([ 33.,  44.,  64.,  74.]), (0, 2, 0): array([ 36.,  47.,  67.,  77.]), (0,): array([ 85.,  98.]), (1,): array([ 39.,  49.,  69.,  79.]), (0, 0): array([  0.,  11.,  22.])}
Result - Lists: [array([ 85.,  98.]), array([  0.,  11.,  22.]), array([ 33.,  44.,  64.,  74.]), array([ 36.,  47.,  67.,  77.]), array([ 39.,  49.,  69.,  79.])]

I think that I could be great to have a type of node that takes care of everything, asking you to just write a single function, knowing exactly which kind of data you will receive. In this case just pairs of numpy arrays:

def myFunction(list_1,list_2):
    return list_1 + list_2

What I didn't specified is the possibility to chose in the code the desired type of data: Item, List or DataTree:
immagine

Here is my question again:
Assume that I want to test it creating some Sverchok Nodes, do you think that it could make sense to create a subclass of the existing Node with some specific methods? In this case I also need a couple of convertion nodes: Lists2Tree and Tree2Lists in order to make it compatible with the existing Sverchok ecosystem...

@nortikin
Copy link
Owner Author

nortikin commented Nov 3, 2018

for sure we need general solution for all nodes. And i am ready to spend my winter holidays to make all adaptation. But we need to develop now working core subclass, that override all classes of nodes. Lets do it in sverchok under 2.8 blender. Sverchok 0.6

@nortikin
Copy link
Owner Author

nortikin commented Nov 3, 2018

@alessandro-zomparelli Lets be clear. What example of use tree from life? Using GH i never used non-equal trees, because there was no reason for me.

@alessandro-zomparelli
Copy link
Collaborator

Hi @nortikin this is a great news! Thanks for all your effort!

Regarding your question, in my experience I have used trees with different size like this:
[ [ [0,1,2,3], [0,1,2,3] ], [ [0,1,2,3] ] ]
I think that it's quite common if you have for example different layers with different number of curves with different number of vertices.

While it's really RARE to have something like this:
[ [ [0,1,2], [3,4], 5, [ [6,7] ], 8 ], [9] ]
The reason why I was considering it, was as a stress test, in order to see if the solution that I was looking for could be proven stable. The final solution can be anyone, but I think should prevent any possible error, for example, if there are some data structures that can't be used, then it shouldn't be allow to make them... Is it reasonable?

So far I've thought about 3 possible strategies:

Lists of Lists (actual solution):
Is flexible, but digging in such structure can be computationally expensive. Also scripting components that work with such structures is not really flexible, because you don't know exactely the data structure... This is something that I found tricky while scripting nodes.

Numpy
Efficient, but very rigid... I'm worried that it could cause a lot of restrictions... See what happen with two list of numbers of different length:

>>> import numpy
>>> a = numpy.array(((0,1,2,3),(4,5,6)))
>>> a
array([(0, 1, 2, 3), (4, 5, 6)], dtype=object)

>>> a+a
array([(0, 1, 2, 3, 0, 1, 2, 3), (4, 5, 6, 4, 5, 6)], dtype=object)

>>> a = numpy.array(((0,1,2),(4,5,6)))
>>> a+a
array([[ 0,  2,  4],
       [ 8, 10, 12]])

Dictionary + Numpy
Maybe is just a theory, but I think that combining keys of a flat dictionary could be more efficient than moving inside many levels of nested lists. (Maybe I'm wrong, I haven't done any benchmark yet).
Instead of one list for each key we can use numpy array, making it slightly faster for numerical operations and preventing numpy errors (see example above), because all numpy array will be 1D.
Working with scripting can sound a bit wierd, but at the end you have a list of all the keys/path of your lists/numpy_array.

Maybe at the moment I'm too focused in the Dictionary approach and I don't see different ways to combine efficently the current Lists. In this team there are great pythonist that maybe have much more elegant solutions than this.

Maybe just a list of numpy array, is enougth, but that doesn't allow to remove levels like you do with paths, because is destructive...

@nortikin
Copy link
Owner Author

Got it. As for me, there should be some mathimatician that can know solution (graphs theory/ matrix-fortran-numpy or any other). Lets find someone somehow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants