Sverchok / SverchokRedux

A reimplementation of Sverchok
GNU General Public License v3.0
4 stars 2 forks source link

Where to start? : ) #7

Closed zeffii closed 9 years ago

zeffii commented 9 years ago

I am practically itching to get started on this...

I was reading about Hylang (A form of Lisp that compiles to Python), it uses an 'abstract syntax' as an intermediate representation of the code, the Lisp is translated to Python using this route (i think):

Hylang -> Abstract Syntax Tree ( a s t )-> Python byte code

I'm not sure how well this translates to what we want the future of Sverchok to be, but it's worth to pause for a while to consider something @ly29 said some time ago (paraphrased): "Sverchok is almost a language in its own right". Granted you have to look at it a bit more abstract than the Node tree you see in the Node editor. but every node tree is a definite, predictable (usually), program with flow control.

It's to time experiment with an intermediate representation of the Node tree

Blender node_tree <---> abstract node_tree -> DAG -> Python (byte code?)
                                ^
                                |
                                V
                             Json IO 

DAG
The purpose of the DAG would be to construct the correct execution order for the Nodes, and output one or more executable entities which can be run in Sequence or Parallel.

abstract node_tree
This serves multiple purposes

More later

ly29 commented 9 years ago

This pretty much aligns with my thinking.

zeffii commented 9 years ago

kind of tricky to start bootstrapping this. Let's start with a very non functional Custom NodeTree with no more than a couple nodes (one of which being a Debug print/console node), that way we can

I don't think we even need more than 1 socket type to begin with..

zeffii commented 9 years ago

I think all the kludge has been added now to __init__.py and node_indexing.py ( formerly called menu.py ... i was never happy with this name) . it seems like some of the uglier stuff there is necessarily ugly ( but it could in some way be pushed into sub modules and imported and called from init ) --

Should we care? this isn't the portion of sverchok that I have the biggest desire to tear down. it works, and is write once use many.

ly29 commented 9 years ago

Concretely what needs to happen from the low level dict that represents a node tree?

Writing this above one step is longer and needs more formal declaration. Some consideration into what is a valid programs needs to be done, the nodes links does create a quite clear picture of what is going on considering node groups and special contexts some further thoughts needs to be put down. Especially considering nodes that control things, overtook only has two such node cases, node groups and switch which works inside existing framework of what nodes, now such nodes should be treated as special symbol nodes, I think.

@zeffii A separate indexing step is one thing that is need, a lot of organizing to be done. Right now it is a bit fragile.

zeffii commented 9 years ago

@ly29 currently i'm not deeply engaged with the code you've written (for reasons explained previously). The abstractions seem to be clean enough. The real test will be to implement more complex nodes, propose a trig node (float -> [sin(float), cos(float), 0]) for fast circles.. a drawing node is a big task, so perhaps a proper pydata->objmesh node for quick and dirty testing. (Plenty of existing code to ransack, but I understand if you want to take a new approach (especially to the naming))

ly29 commented 9 years ago

First step is just debug print the nodes I have now actually running, then gradually add complexity. I expect to do a lot of copying from Sverchok since know my way around the code base quite well.

Tentatively which are the special nodes that require knowledge from the system and aren't just functions that execute?

Once something is up and running things to do include documentation, writing more node, thinking about interaction with blender.

An OpenGL viewer is a big topic, and something to look forward to experiment with, sadly this is still some time away.

ly29 commented 9 years ago

Some further thinking gives the following order:

  1. Get it running with data
  2. Add symbols/understanding.

While meditating work on menu, node indexing or documentation.

zeffii commented 9 years ago

so would the mathnode be declared something like

import numpy as np

from ... import math_node_modes
from ... SvRx.types import mode_enum

def math(x: np.array = 0.0, y: np.array = 1.0, mode: mode_enum = 'add') -> [("res", np.array)]:
    return math_node_modes(mode, (x, y))

where mode_enum is just a class to indicate that that parameter will delegate to some enum/dict

ly29 commented 9 years ago

I have been thinking about how to combine different function into one logical ui, as stated above is one approach but I don't feel so happy about it. We also have some nice ui additions on the current math node where it show different things depending on connections etc. Even though we can certainly create some logic to auto generate this, but perhaps having handwritten ui classes for certain nodes isn't a bad idea.

So pure ui class that resolve into appropriate functions at a later stage. But I think what to do will become clearer when the other pieces fall into place.

However, the above should most definitely be supported anyway.

zeffii commented 9 years ago

i'm just trying to get a better understanding of where this is going regarding multi-mode nodes, or if nodes will be hot-swapped in place to a dedicated MathAdd node, if there's a mode change.

zeffii commented 9 years ago

I think I agree that certain nodes can be suitably handwritten, at least initially as a boot strap step.. eventually, if a more automated way becomes clearly more convenient we migrate.

The upside is we don't have backward compatibility limitations for the foreseeable future. I expect and hope stuff will break in the name of progress.

ly29 commented 9 years ago

Everything will break. Backwards compatibility is a thing for the past. Honestly, surpassing the current Sverchok is a big project, but with better standards and auto generation of boring code it is possible. A lot of time was spent avoiding breaking things because some thing weren't well defined.

Just keeping the i/o standards of each node will prove invaluable.

But first boot strapping...

ly29 commented 9 years ago

skarmavbild 2015-11-20 kl 16 34 14

Execution of simple functions working.

>>> layout = ng.serialize()
>>> node = SvRx.core.compiler.compile(layout)
>>> node.execute()
Entering debug_print
Entering add
Entering Linear space
Entering Range
[  0.           1.11111111   2.22222222   3.33333333   4.44444444
   5.55555556   6.66666667   7.77777778   8.88888889  10.        ]

Next step include support for data tree, and type matching.

ly29 commented 9 years ago

There isn't any direct reaction to ui actions. Python console is needed for running things. Also note that the different steps right now correspond to mostly to the graph even if it don't use the AST module. Still needs some more grease between the execution units...

zeffii commented 9 years ago

i don't have any sensible questions, but i'm glad to see you're juggling these concepts and still catching them :)

ly29 commented 9 years ago

It is very incremental right now, small steps forward. But a sensible structure, or at least much more so than the old mess... skarmavbild 2015-11-20 kl 18 53 50 Two steps, here, matching different list lengths and only executing each node once. Also, as a implementation detail no socket values exist but special value node are implemented. Not so sure about that.

ly29 commented 9 years ago

The graph, the value nodes are named "node.socket". The graph is sorted by socket order.

'debug_print'
    'add'
        'add.002'
            'Range'
                'Range.start'
                'Range.stop'
                'Range.step'
            'add.003'
                'add.001'
                    'Linear space'
                        'Linear space.start'
                        'Linear space.stop'
                        'Linear space.count'
                    'add.001.y'

The execution order is:

Range.start
Range.stop
Range.step
Range
Linear space.start
Linear space.stop
Linear space.count
Linear space
add.001.y
add.001
add.003
add.002
add
debug_print
nortikin commented 9 years ago

nice and clean

ly29 commented 9 years ago

Side step to autoload and auto create menu from available nodes. Based on http://stackoverflow.com/a/25562415

ly29 commented 9 years ago
def import_submodules(package, recursive=True):
    """ Import all submodules of a module, recursively, including subpackages
    :param package: package (name or actual module)
    :type package: str | module
    :rtype: dict[str, types.ModuleType]
    """
    if isinstance(package, str):
        package = importlib.import_module(package)
    results = {}
    for loader, name, is_pkg in pkgutil.walk_packages(package.__path__):
        print(name)
        full_name = package.__name__ + '.' + name
        results[full_name] = importlib.import_module(full_name)
        if recursive and is_pkg:
            results.update(import_submodules(full_name))
    return results

Then in nodes we build a node_dict which is a central repository of node data. From this we build the menu, recording category, class and function associated with each bl_idname, think about populating with more metadata like types etc.

skarmavbild 2015-11-22 kl 14 58 02

So right now, all that is needed for adding a node is a adding .py file with the right info into a subdirectory of nodes. A class will be created with ui and the menu will be populated.

zeffii commented 9 years ago

and with this it is possible to do a reload by unloading package and then reloading?

ly29 commented 9 years ago

Maybe, haven't tested that, I just do a reload on the imported modules. works anyway.

ly29 commented 9 years ago

Working if node.

Compile/execution system needs some cleaning up... Especially the flow of data need some proper cleaning up.

Todo:

With that I would consider bootstrapping and getting started done.

ly29 commented 9 years ago

Developing nodes flow: skarmavbild 2015-11-22 kl 18 25 09 Create a file called trig.py in nodes/math with:

import numpy as np

def sin(x: np.array = 0) -> [("res", np.array)]:
    return np.sin(x)

def cos(x: np.array = 0) -> [("res", np.array)]:
    return np.cos(x)

SvRxFunc = [sin, cos]

And press F8... voila. skarmavbild 2015-11-22 kl 18 31 16

ly29 commented 9 years ago

The SvRxFunc = [sin, cos] declaration of which function to create nodes from could become optional using inspect. Using importlib magic, blender texts files could be considered modules and imported under a user label. Making special code script node practically not needed, (except importlib trickery), more like a simple user interface thing.

nortikin commented 9 years ago

can we make somehow to user zoom on error nodes? we can make list of error nodes, and button for zoom, than it can orient on layout and center view. What do you think? or better to find nodes by name? not sure. for noob better automate.

ly29 commented 9 years ago

Yes automatic is a better. The whole user interface is thing that could be redone much smoother I think, for a later stage.

nortikin commented 9 years ago

should i draw draft of new menus? Should we do shift+A menu call as in AN is

ly29 commented 9 years ago

I have not played around with AN in a long while, which I probably should do a bit.

Start a new issue for ui issues so the discussion does not get lost here.

ly29 commented 9 years ago

Infinitely auto multiplying levels. Working but not verified. skarmavbild 2015-11-23 kl 16 40 38

A I suspected needs a little bit more work.

zeffii commented 9 years ago
def sin_cos(x: np.ndarray = 0) -> [("sin", np.ndarray), ("cos", np.ndarray)]:
    return np.sin(x), np.cos(x)

:)

ly29 commented 9 years ago

skarmavbild 2015-11-23 kl 21 18 40 Two return values doesn't really work yet. Not that anything really works yet. But the class from function is really nice and autoloading is really nice, no?

zeffii commented 9 years ago

Yep, seems lightweight. Will love to get to work on nodes once the graph is complete. I don't want to interfere with this.

Array math node, how will that look (internally) , my implementation in FLOW was a little convoluted.

ly29 commented 9 years ago

I'm struggling a bit with types at the moment. How to deal with them...

Please show some code, and I can comment.

zeffii commented 9 years ago

https://github.com/BlendCollider/FLOW/blob/master/nodes/operations/np_math.py

zeffii commented 9 years ago

I can see now that that a lot of the code should have been handled in the sockets somehow, and abstracted away from the working code. It was a very simple and dumb execution model.

ly29 commented 9 years ago

Yeah, that is thing I am struggling with. I resisting, for some reason, to write it that verbose but struggling for a cleaner version. I think I have to look a bit at type handling systems in python...

Also I am not happy with any of the ideas for socket handling. The most temping right now is to have a one Node for every math op with a switch that switches between them and reconnecting etc...

def match_length(args):
    lengths = [len(arg) for arg in args]
    if lengths.count(lengths[0]) == len(lengths):
        return args
    max_len = max(lengths)
    out = []
    for arg, length in zip(args, lengths):
        if length < max_len:
            new_arg = np.zeros(max_len)
            new_arg[:length] = arg
            new_arg[length:] = arg[-1]
            out.append(new_arg)
        else:
            out.append(arg)
    return out
def recursive_map(func, args, inputs_types, level=0):
    if level == 0 and isinstance(args, inputs_types[0]):
        return func(*args)

    checked = [isinstance(arg, types) for arg, types in zip(args, inputs_types)]

    if all(checked):
        return func(*args)
    if any(checked):
        new_args = [repeat(arg) if check else arg for check, arg in zip(checked, args)]
    else:
        new_args = args

    return [recursive_map(func, arg, inputs_types, level + 1) for arg in zip(*new_args)]
ly29 commented 9 years ago

Should float be auto converted into int? It is this kind of convenience that suits a node system, having to convert values just lead us to write range(int(start), int(stop)) and similar things in the old Sverchok. These functions are all bit to specific and skips important bits. Oh well.

zeffii commented 9 years ago

The most temping right now is to have a one Node for every math op with a switch that switches between them and reconnecting

It's even tempting to make an abstract text layer for all of this, instead of nodes first. like DSL https://github.com/Sverchok/SverchokRedux/issues/12

zeffii commented 9 years ago

yes, most definitely have a way to auto cast,

zeffii commented 9 years ago

hehe:) image

ly29 commented 9 years ago

First type rule, an int works every a float value works, python and numpy converts as needed.

ly29 commented 9 years ago

skarmavbild 2015-11-24 kl 10 12 01

ly29 commented 9 years ago

UI hooks works, writing nodes works (and has never been as simple). For now I consider this bootstrapped. The state is super alpha and some refactoring needs to be done. Todo urgently is dealing with broadcasting and typecasting. Then more complex nodes can be created, not as in code but as having python properties and ui etc.

A lot of things await.

zeffii commented 9 years ago
import numpy as np

def random_uniform(low: float = 0.0, top: float = 1.0, count: int = 10) -> [("rnd uniform", np.ndarray)]:
    return np.random.uniform(low, top, count)

random_uniform.label = "Random uniform"

SvRxFunc = [random_uniform]

image

zeffii commented 9 years ago

I can't help but feel that there should be a way to use the signature of the numpy functions themselves to bulk generate their nodification. By this I mean we could declare the list of numpy functions that we want to generate a node for and let introspection make the SvRxFunc.. ?

ly29 commented 9 years ago

It is an interesting approach. Could certainly work quite well for the nodes with 1:1 mapping to numpy concepts

ly29 commented 9 years ago

I guess by parsing the .__doc__ attribute.

print(numpy.random.uniform.__doc__)
zeffii commented 9 years ago

yeah hmmmm it's tempting..

zeffii commented 9 years ago

it's a finite function base , so perhaps manual declaration isn't too bad.. the parsing code might be longer than the manual aprroach