Request for input: VTK/Python API

Hi all,

In this post, I introduced some of the changes we are making to the VTK wrappers. One of these is the introduction of an operator to easily connect sources and filters. I initially proposed using >>. Later | was suggested:

(source >> vtkContourFilter(contours=[10,20]) >> vtkShrinkFilter()).execute()

vs

(source | vtkContourFilter(contours=[10,20]) | vtkShrinkFilter()).execute()

Can we have an informal vote on which one is better?

Thanks.

1 Like

I go back and forth on this.

The pipe | symbol is well known especially for linux users. The direction of data flow is implicit.

The >> symbol points in the direction of data flow which is a neat bit of information to add in, especially for beginners. Also the >> is more visually apparent. So >> is my vote, unless there is some weird python syntax or pattern that gets in the way etc.

Python already overrides “|” for a different purpose:

>>> {1,2,3} | {3,4,5}
{1, 2, 3, 4, 5}

My vote is for “>>”, because it’s a rarely used operator. Plus all the reasons that Will gave.

But I’m also in favor of something that takes a list of filters (and data objects) and generates a new algorithm object that represents a chunk of pipeline:

pipeline = vtkPipeline([vtkContourFilter(contours=[10,20]), vtkShrinkFilter()])
output = pipeline1.execute(input_data)

output = vtkPipeline([input_data, pipeline]).execute()
1 Like

I’ve implemented >> using the number protocol (tp_as_number). fyi, it’s possible to have both >> and | :slight_smile: But, right now, only >> is implemented.

See TestAlgorithmNumberProtocol.py.

In the MR, you can see a large number of operators which are all currently null. We’ve a lot to pick from.

Obviously so. But just because you can, doesn’t mean you should :slight_smile:

1 Like

How do you specify whics output port is connected to which input connection?

I would not introduce a new method of specifying VTK filter connections if that method only works filters that have a single input and a single output.

What we want is a simple syntax for describing a graph. The nodes are the filters, each node has properties. The edges are the connections, each edge has properties to specify the output port number and the input port number.

Note than when we create a filter we can set a name for the filter with SetObjectName(). So we can easily set names for the nodes in the graph.

I agree. I have been thinking about this. The simplest solution that I can think of is:

OutputPort(aFilter, 1) >> anotherFilter
aFilter >> InputPort(anotherFilter, 1)
aFilter >> Append(appendFilter)

Implementing these in Python is trivial.

I do have a related question. Let’s say that we implement some utility functions/classes like this and we want them to be included when one includes the vtkCommonExecutionModel module. How would one do that? Change the vtkCommonExecutionModel to something like this?

vtkCommonExecutionModel.py

from .vtkCommonExecutionModelCompiled # this is the .so file. We need to rename it I guess?

def OutputPort(...):
    ...

We can provide those functions from the .so file instead of doing it in python. Just like execute is generated in vtkAlgorithmPython.cxx, the wrapper would generate new functions in C++ that return an output port for an algorithm.

would something like this be possible?

AFilter().output_port(1) >> BFilter()
AFilter() >> BFilter(input_port=1, ...)

In the current version, I have:

filter1 >> SelectPorts(filter2, input_port=1, output_port=1) >> filter3

I am not happy with this either. We can do (and I like):

AFilter().output_port(1) >> BFilter()

This is not feasible:

AFilter() >> BFilter(input_port=1, ...)

If the input_port is passed as a constructor argument, it would become intrinsic to the filter which affects future connections. Also, I like the idea of everything flowing left to right (meaning, the input port selection should come before the filter). So how about this:

AFilter() >> input_port(1, BFilter())

In its full complexity, this would look like:

AFilter() >> input_port(1, BFilter()).output_port(1) >> CFilter()

Btw, this way of connecting things would not be used much, I would guess. This would likely be more common:

line = vtkLineSource(...).execute()
dataset >> vtkStreamTracer(source_data = line)

or

line_source = vtkLineSource(...)
dataset >> vtkStreamTracer(source_connection = line_source.output_port)
1 Like

I like

AFilter() >> input_port(1, BFilter()).output_port(2) >> CFilter()

another “visually symmetric” options could be:

AFilter() >> select_ports(1, BFilter(), 2) >> CFilter()

AFilter() >> select_ports(1, BFilter(), 2) >> CFilter()

I like this a lot. Not only because it’s concise and clear but also it would be a lot less disruptive to implement.

Have you considered a method chaining approach that doesn’t introduct a new operator by using a function to wrap the filter, such as…

(ported(AFilter())
    .port(1, BFilter())
    .port(2, CFilter())
    )

this doesn’t introduce any new methods to the filters themselves

Perhaps a monad like object that wraps the filter would be better

(Ported(Afilter())
    .port(1, BFilter())
    .port(2, CFilter())
    ).filter()