Welcome to Cadabra Q&A, where you can ask questions and receive answers from other members of the community.
+1 vote

Consider the following:

{a,b,c}::Indices(vector, position=independent).
{i,j,k}::Indices(isospin, position=independent).

bool1 = $\Lambda_{a}$.matches($\Lambda_{i}$);

bool2 = $\Lambda_{a}$.matches($\Lambda^{i}$);

bool1 evaluates to False, while bool2 evaluates to True.

The same behavior occurs if we use position=fixed. However, for position=free, both evaluate to False (as expected). This would seem to be a bug.

in Bug reports by (1.0k points)

1 Answer

+1 vote

Pattern matching has a lot of different branches to handle these days... The underlying C++ matcher returns a variety of more subtle answers (not just true or false) and I did not map that to true/false correctly.

Now fixed in github.

by (80.3k points)

Hmm. This version seems to have introduced a new bug (or new behavior).

Before,

$\Lambda_{a}$.matches($\Lambda_{b}$);

would return True. Now it returns False.

I was a bit too strict mapping the many return values of Ex_comparator to a boolean. I have now restored the old behaviour (reporting a match for indices with different names but coming from the same set).

I should probably add a flag exact_match to give the behaviour where

$\Lambda_{a}$.matches($\Lambda_{b}$);

returns False, as this might be useful too. Or simply have a more general matching function which does not return a bool but the full set of return values used by Ex_comparator.

(I wasn't sure if that was a bug or how you want .matches() to behave.)

Is .matches() the canonical way to do the sort of comparison where you want to match index type?

By the way, I also noticed in https://cadabra.science/notebooks/ref_programming.html (and in the book pdf) that you do refer to the strict behavior of .matches() (requiring it to match index type and name) although you show

$A_{m n}$.matches($A_{k l}$)

returning True in your example.

Does Python have access to Ex_comparator or is that purely on the C++ side?

Well, matches used to return True for

$A_{m n}$.matches($A_{k l}$)

so I think that should be kept as default behaviour (and the docs changed if they disagree). But it makes sense to give that function an extra argument exact_match which defaults to False, and would require index names to be exactly equal when True.

The Ex_comparator does not exist on the Python side (yet).

OK, that makes sense.

The reason I'm interested (among others) is that I'm trying to implement a multizoom function in Python that takes a list of rules to match against, and zooming to those terms that match at least one of the rules.

I am running into a problem that matches() maybe isn't as clever as I want it to be. For example,

$x A??$.matches($x y$)
$x A??$.matches($x y z$)

returns True and False, respectively, rather than both True. Also, the order matters, so that

$x A??$.matches($y x$)

returns False. Is this the desired behavior?

If matches isn't clever enough, is there some other Python routine that can do a more sophisticated pattern matching?

EDIT: I have just discovered the .compare routine in ExNode. Maybe this will help...

Precisely for these kind of things Ex_comparator has functions like match_subproduct and match_subsum. I just never wrote anything like that on the Python side.

Those examples all work as intended, even though I understand the results are not particularly useful for you.

ExNode.compare(...) may get you a bit further, but will probably still not be enough. Let me know, it may be time to export Ex_comparator.

After taking a peak at your implementation of zoom.cc, I realize I could just do the same thing on the Python side: recast the list of patterns as dummy rules and then just use substitute and see if it does anything. But since Python doesn't have access to can_apply, I think this may involve making copies of each ExNode. I've no idea how slow that will be.

I may also see if I can modify zoom.cc to handle a list of rules. From your implementation, it seems all I need to do is to change the code that wraps the rules. But that requires me to understand more C++. :-)

Thanks for handling all my recent questions, by the way. I've been trying to port my old very bad code for arxiv:1609.09083 and make it cleverer (as an excuse to learn python).

I think this is trivial to do on the C++ side by modifying zoom.cc, and useful as an addition anyway. If you don't feel digging into that, let me know and I will do it later tonight.

Hi Kasper. I wasn't sure I should be so presumptuous to put this somewhere on Github, but I think the following does the trick.

In zoom.cc, replace zoom::zoom contents with:

// Convert rules into a list (if it isn't already)
rules = cadabra::make_list(rules);

// Iterate over the list
cadabra::do_list(rules, rules.begin(), [&](Ex::iterator it) {
    // Create a proper substitution rule out of the pattern (otherwise
    // substitute will not swallow it).

    auto wrap = rules.wrap(it, str_node("\\arrow"));
    rules.append_child(wrap, str_node("dummy"));
    return true;
});

This acts as

ex := A1 x+A2 y + A3 z.
zoom(ex, ${x A??, y A??});

and returns

A1 x + A2 y + ...

It also preserves the original behavior if the argument is not a list.

See, that wasn't so hard ;-) I have pushed this to github now.

...