1

I'm trying to use the answer from this question and came across some interesting behaviour:

from sympy import Symbol


class MySymbol(Symbol):

    def __new__(cls, symbol, description):

        obj = super().__new__(cls, symbol)
        obj.description = description
        return obj


symbolx = MySymbol('x', 100)
symbolx2 = MySymbol('x', 200)
symboly = MySymbol('y', 300)

print(symbolx.description)
print(symbolx2.description)
print(symboly.description)

returns:

200
200
300

Instead of the expected

100
200
300

My best guess is that sympy returns the same Symbol for a given name. Is there any way to work around this?

Nathan
  • 3,558
  • 1
  • 18
  • 38
  • 2
    I don't understand how you would even use this. If I later had an expression `'x + y'` which `x` symbol should sympy use? – Cory Kramer Aug 11 '22 at 12:20
  • @CoryKramer I could go into the details, but it's a bit complex and I'm afraid it'll only make the question more confusing. I've really tried to boil it down the most basic problem. But in short, I intend to pass `symbolx + symboly` instead of `x + y` – Nathan Aug 11 '22 at 12:23
  • The problem is that you're two symbols compare equal. The cache will return the same object. You could subclass Dummy instead of Symbol. I don't understand why you don't just give the symbols different names though and really I think that there are better ways of associating information with your symbols than using a subclass. Broadly I recommend taking a different approach to your overall problem. – Oscar Benjamin Aug 11 '22 at 13:05
  • @OscarBenjamin see my latest answer, is what you meant? – Nathan Aug 11 '22 at 13:20

3 Answers3

2

Based on @OscarBenjamin s comment, using Dummy rather than Symbol:

from sympy import Dummy

class MySymbol(Dummy):

    def __new__(cls, symbol, description):
        obj = super().__new__(cls, symbol)
        obj.description = description
        return obj


symbolx = MySymbol('x', 100)
symbolx2 = MySymbol('x', 200)
symboly = MySymbol('y', 300)

print(symbolx.description)
print(symbolx2.description)
print(symboly.description)

Returns:

100
200
300
Nathan
  • 3,558
  • 1
  • 18
  • 38
  • An advantage here over DescSymbol is that you retain ability to do manipulations: `symbolx + symbolx2 -> x + x` – smichr Aug 11 '22 at 13:46
  • @smichr With my answer you still can do all the operations using e.g. `symbolx.s + symbolx2.s` - and it actually performs the add, resulting in `2*x` compared to `_x + _x` using `MySymbol(Dummy)` – matszwecja Aug 11 '22 at 13:54
  • @matszwecja `performing the add` in this context is a bug, not a feature – Nathan Aug 11 '22 at 14:06
  • @matszwecja, the nice thing about subclassing from Dummy is that you don't need to use the `s` attribute. This results in latex appearing as unevaluated Add without the need of creating an actual unevaluated Add. – smichr Aug 11 '22 at 15:17
0

My suggestion would be to create a wrapper object instead of inheriting Symbol. This way you may store various data while still keeping all of the sympy functionality.

from sympy import Symbol, Add

symbols = []

class DescSymbol:
    def __init__(self, symbol, description):
        self.s = Symbol(symbol)
        self.description = description


symbolx = DescSymbol('x', 100)
symbolx2 = DescSymbol('x', 200)
symboly = DescSymbol('y', 300)

print(id(symbolx.s))
print(symbolx.description) # 100
print(id(symbolx2.s)) # same as symbolx.s
print(symbolx2.description) # 200
print(id(symboly.s)) # unique
print(symboly.description) # 300
matszwecja
  • 6,357
  • 2
  • 10
  • 17
-1

The solution I currently have is to append white space to the end of the symbol to ensure it's unique:

from sympy import Symbol

symbols = []

class MySymbol(Symbol):

    def __new__(cls, symbol, description):
        while symbol in symbols:
            symbol += ' '
        symbols.append(symbol)
        obj = super().__new__(cls, symbol)
        obj.description = description
        return obj


symbolx = MySymbol('x', 100)
symbolx2 = MySymbol('x', 200)
symboly = MySymbol('y', 300)

print(symbolx.description)
print(symbolx2.description)
print(symboly.description)

This returns:

100
200
300

I'll keep the question open in case anyone comes along with a more elegant answer

Nathan
  • 3,558
  • 1
  • 18
  • 38
  • What's the point of using `x ` and not something like `z` then? You realise `x` and `x ` are different symbols that can't be added together? – matszwecja Aug 11 '22 at 12:54
  • @matszwecja spaces don't show up in LaTeX math enviornment. Like I've said; I've boiled the problem down to bare bones (because if I include all the details people won't read it) – Nathan Aug 11 '22 at 13:09
  • Yeah but they will matter for all the Python preprocessing you will try to do before LaTeX will come into play. – matszwecja Aug 11 '22 at 13:13
  • @matszwecja I did some tests and I haven't been able to find any such effects – Nathan Aug 11 '22 at 13:21
  • Try a simple example of `print(Add(Symbol('x'), Symbol('x')))` vs `print(Add(Symbol('x'), Symbol('x ')))` - one outputs `2*x` and the other `x + x ` – matszwecja Aug 11 '22 at 13:23
  • @matszwecja I'm afraid we're now going outside of the scope of the question, I would be using it as `x = Symbol('x'); y = Symbol('x '); print(Add(x, y))` Thanks for your concern though! – Nathan Aug 11 '22 at 13:50
  • That's pretty much exactly the same to what I wrote in the comments and it does not actually perform the add. – matszwecja Aug 11 '22 at 13:55
  • @matszwecja Exactly, that's the intended behaviour. It would be kind of weird to add two different values that happen to have the same symbol. – Nathan Aug 11 '22 at 14:05
  • Why you'd want to have two different values use the same symbol is beyond me in that case. – matszwecja Aug 11 '22 at 14:20