How to compare Theano variables?

86 views
Skip to first unread message

Olivier Delalleau

unread,
Nov 25, 2011, 12:26:39 PM11/25/11
to thean...@googlegroups.com
>>> x = tensor.scalar()
>>> (x + 1) == (x + 1)
False

Is there another comparison operator somewhere (I really want a boolean output, not a symbolic thing).

-=- Olivier

Frédéric Bastien

unread,
Nov 25, 2011, 12:34:48 PM11/25/11
to thean...@googlegroups.com
The only way is to compare the graph. From memory there is not such a
general implementation. How scan do it? Maybe could reuse that code?

If you look only a a chain of var, there is something like chain_check
or check_chain that can help you.

What are you trying to do?

Fred

Olivier Delalleau

unread,
Nov 25, 2011, 12:49:28 PM11/25/11
to thean...@googlegroups.com
I'm testing some optimization. I factored out some core function that outputs a data structure (list of lists) with Variables in them.
I want the test to compare this output to the expected output.
For instance:
    assert my_core_function(x) == [True, [x + 1]]
where x would be a tensor variable.
Currently my test is calling str() before the comparison, but as a result it doesn't entirely compare the graphs.

-=- Olivier

Razvan Pascanu

unread,
Nov 25, 2011, 1:08:15 PM11/25/11
to thean...@googlegroups.com
There is a function that compares graphs used by scan. 
The function is in theano/scan_utils.py The function is called equal_computations. Here is its signature:

def equal_computations(xs,ys,in_xs = None, in_ys = None, strict=True)

It compares the graphs represented by xs variables with those in ys. You can provide inputs to those graphs, and if you do so (using in_xs, in_ys), 
the inputs are not compared, just the graph constructed on them. I do however check if the inputs have the same type. 

strict has sort of the same meaning. If false, in only checks that the inputs have the same type. I'll add some more documentation and do a pull request. 

Razvan

James Bergstra

unread,
Nov 25, 2011, 1:23:44 PM11/25/11
to thean...@googlegroups.com
Maybe what you want is the merge optimizer? If the graphs can be
merged, True else False.

Olivier Delalleau

unread,
Nov 25, 2011, 1:35:48 PM11/25/11
to thean...@googlegroups.com
Thanks, is this scan function actually calling the merge optimizer under the hood?

-=- Olivier

2011/11/25 James Bergstra <james.b...@gmail.com>

Razvan Pascanu

unread,
Nov 25, 2011, 1:54:13 PM11/25/11
to thean...@googlegroups.com
On Fri, Nov 25, 2011 at 1:35 PM, Olivier Delalleau <dela...@iro.umontreal.ca> wrote:
Thanks, is this scan function actually calling the merge optimizer under the hood?

No. 

One thing that I can say is that calling an optimization means you need to have an env. And you actually need to have both graph in the same env. Also the Merge optimizer is a global one. I'm not saying that these two can not be refractored, just that doing that might not be extremely straight forward. 

I either didn't had the knowledge, or was to lazy when I wrote this equal_computation function.

Razvan

James Bergstra

unread,
Nov 25, 2011, 2:53:49 PM11/25/11
to thean...@googlegroups.com
It shouldn't be too hard... it should be something like this:

y1 = x + 1
y2 = x + 1
e = Env([x], [y1, y2])
MergeOptimizer().optimize(e)
return y1.owner == y2.owner

Frédéric Bastien

unread,
Nov 25, 2011, 2:56:05 PM11/25/11
to thean...@googlegroups.com
But what if they don,t use the same input variable? This case is
covered by RP function.

Fred

James Bergstra

unread,
Nov 25, 2011, 3:03:45 PM11/25/11
to thean...@googlegroups.com
Right, to use MergeOptimizer, you have to use aligned inputs.

You can get *all* the inputs to each of y1 and y2 by
y1_inputs = gof.graph.inputs(y1)
y2_inputs = gof.graph.inputs(y2)

e = Env(y1_inputs + y2_inputs, [y1, y2])
# TODO: clone here like std_env() does
for i1, i2 in zip(y1_inputs, y2_inputs):
e.replace(i1, i2)
MergeOptimizer().optimize(e)
return e.outputs[0].owner == e.outputs[1].owner


2011/11/25 Frédéric Bastien <no...@nouiz.org>:

Olivier Delalleau

unread,
Nov 25, 2011, 3:47:23 PM11/25/11
to thean...@googlegroups.com
So... would it be a good idea to implement Variable.__eq__ to do this?

Olivier Delalleau

unread,
Nov 25, 2011, 4:33:39 PM11/25/11
to thean...@googlegroups.com
I guess not, since we use dictionaries of variables that rely on "==" returning the same thing as "is" (for updates of shared variables for instance).

-=- Olivier

2011/11/25 Olivier Delalleau <dela...@iro.umontreal.ca>

James Bergstra

unread,
Nov 25, 2011, 7:34:15 PM11/25/11
to thean...@googlegroups.com
You're right, it's a convenient thing to be able to do, but there's
equals, and there's equals, and there's equals, if you know what I
mean. I think the '==' operator currently does the right thing (and
yes, we need it to support vars as dictionary keys)

On Fri, Nov 25, 2011 at 4:33 PM, Olivier Delalleau

Olivier Delalleau

unread,
Nov 25, 2011, 11:39:30 PM11/25/11
to thean...@googlegroups.com
It looks like the MergeOptimizer approach will modify my input variables though... can it be avoided?

James Bergstra

unread,
Nov 26, 2011, 9:26:13 AM11/26/11
to thean...@googlegroups.com
It will modify the elements of the env, you should copy the subgraph
in question.

On Fri, Nov 25, 2011 at 11:39 PM, Olivier Delalleau

James Bergstra

unread,
Nov 26, 2011, 9:33:19 AM11/26/11
to thean...@googlegroups.com
That would be really nice to have a version of this function, it would
be useful in so many optimization tests... I can hardly believe this
function doesn't already exist!

Razvan Pascanu

unread,
Nov 26, 2011, 9:46:10 AM11/26/11
to thean...@googlegroups.com
There is the function I pointed out from scan. It does exactly this. Granted the documentation wasn't that well written.

I see though how that function could be re-written in terms of the MergeOptimizer. I guess if we decide to do this we should eliminate the one in scan_utils. 

Is using the MergeOptimizer adding unwanted overhead to this operation? Imagine if we have to apply it many many times. If not, we should make a ticket to remove the function in scan_utils and replace it with one in terms of MergeOptimizer.

Razvan

Olivier Delalleau

unread,
Nov 26, 2011, 12:58:20 PM11/26/11
to thean...@googlegroups.com
I was thinking that maybe, as a first step, such an equality function could actually call both versions and verify they return the same output. It would be an additional check to ensure they both work properly.
I added such a function in theano.gof.graph that you can check here: https://github.com/delallea/Theano/commit/d19531723ea96f00a7a75d50b9735bdc75226349

I went for a simpler signature than equal_computations to make it easier to type for the most generic case where we just want to compare two graphs. I changed the name (to "is_same_graph") to avoid confusion, but I don't really have a strong preference for a specific name, so if you prefer something else let me know. I added a merge-based implementation that this function calls, and compares to the output of equal_computations (this is a just a sanity check that could be removed after a while).

Let me know what you think about it.

-=- Olivier

2011/11/26 Razvan Pascanu <r.pa...@gmail.com>

Olivier Delalleau

unread,
Nov 26, 2011, 1:17:55 PM11/26/11
to thean...@googlegroups.com
Is it a bug in equal_computations that if I try to compare x with itself, where x is a tensor.vector(), it returns False?

-=- Olivier

2011/11/26 Olivier Delalleau <dela...@iro.umontreal.ca>

Razvan Pascanu

unread,
Nov 26, 2011, 2:03:07 PM11/26/11
to thean...@googlegroups.com
Yes, I send a pull request that fixes that. 
It happens though only when you try to compare a variable to itself, something that scan never does (just compares computational graphs), if anyone wonders on what this bug means for scan. 

Razvan

Frédéric Bastien

unread,
Nov 28, 2011, 4:55:35 PM11/28/11
to thean...@googlegroups.com
Good to know for the release note.

Fred

Reply all
Reply to author
Forward
0 new messages