New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved sub-routine for string quantity
arguments for find_unit
#13875
base: master
Are you sure you want to change the base?
Conversation
Re-written sub-routine for string quantity arguments for find_unit.
quantity
arguments for find_unit
Now covers all possible cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
@@ -222,23 +222,28 @@ def find_unit(quantity): | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think docstring should explain its exact behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ylemkimon, I have made the requested changes, please have a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all instances of units/dimensions with these arguments.
is little ambiguous.
If a string is given, it currently returns
(i) all units matching the string
(ii) if a dimension exactly matching the string exists, all units with that dimension
(iii) if a unit exactly matching the string exists, all units with same dimension
Explained the result for possible 2 input cases: 1. String and 2. Quantity/Dimension
72 character rule
@smichr, can you please review this PR. |
sympy/physics/units/__init__.py
Outdated
if isinstance(dim, Quantity): dim = dim.dimension | ||
for i in dir(u): | ||
quant_attr = getattr(u, i) | ||
if quantity in i or (isinstance(quant_attr, Quantity) and quant_attr.dimension == dim): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it should return Dimension
s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, as name of function find_unit
itself implies to find all instances of that unit. However, original docstring
states that function should return all Dimensions
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A separate function like find_dimension
can be added to return all the dimensions. Please suggest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, the results of these functions (find_unit
and find_dimension
) would be quite alike, which may make this exercise quite dispensable.
Done!! ping @ylemkimon . |
@ylemkimon, ping. |
I think documentation needs improvement, but I'm not a native English speaker, so I cannot think of better one. |
@asmeurer, do you have some suggestions to improve the documentation. |
@@ -210,12 +210,18 @@ | |||
|
|||
def find_unit(quantity): | |||
""" | |||
Return a list of matching units or dimension names. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it would be clearer to just demonstrate by example rather than to write an abstract statement which will be hard to understand unless you already understand it or until you see an example:
"""
Return the list of units that match the given quantity.
Parameters
==========
quantity : string, Dimension, or Quantity
Examples
========
>>> from sympy.physics import units as u
When the quantity is a unit given is a string or unit, ...
>>> u.find('charge') # unit as string
>>> u.find(u.charge) # unit as unit
When the quantity given is a Dimension, ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quite a nice idea, thanks for the insight. Made changes, please check.
Made changes as suggested by @smichr
sympy/physics/units/__init__.py
Outdated
>>> u.find_unit(u.inch**3)[:5] | ||
['l', 'cl', 'dl', 'ml', 'liter'] | ||
|
||
# When the input is a string, it returns units which have input |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two comments:
- go ahead and write the comments without the leading
#
- the last comment about using strings is a bit confusing, perhaps
because of 'sub-script'. I think "substring" would be more recognized in this context. Myabe
When the input is a string, units or Quantities are returned. The units will
have the input as a substring; Quantities will a dimension that matches the
input.
But if that is right, I'm not sure why the behavior of trying to match dimensions was added. That can already be done with u.dimension. string input is used when you aren't sure of the spelling or quantity that you are looking for; once you find it you can then use the corresponding dimension in your next search. Is there an unecessary complication by combining both outputs for a string input? e.g. if you can't do [u.find_unit(gettattr(u, i)) for i in u.find_unit('foo')]
... or soemthing like that ... then this is maybe not a good direction to go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That can already be done with u.dimension. string input is used when you aren't sure of the spelling or quantity that you are looking for; once you find it you can then use the corresponding dimension in your next search.
Hmm.., I have a simple idea for this. We can match the various quantities in the dictionary u
for given string and give outputs like this:
>>> u.find_unit('char')
do you mean: charge # for incomplete entries
>>> u.find_unit('charge')
1 # a perfect match
>>> u.find_unit('0x00x0')
0 # no match
OR
simply an array of suitable matches ['charge']
What do you think about these changes: diff --git a/sympy/physics/units/__init__.py b/sympy/physics/units/__init__.py
index 75de4ee..dc38476 100644
--- a/sympy/physics/units/__init__.py
+++ b/sympy/physics/units/__init__.py
@@ -249,29 +249,38 @@ def find_unit(quantity):
"""
import sympy.physics.units as u
- rv = []
+ rv = set()
+ IA = [(i, getattr(u, i)) for i in dir(u)]
+ IA = [(i, a) for i, a in IA if isinstance(a, (Quantity, Dimension))]
if isinstance(quantity, string_types):
- dim = getattr(u, quantity)
- if isinstance(dim, Quantity): dim = dim.dimension
- for i in dir(u):
- quant_attr = getattr(u, i)
- if quantity in i or (isinstance(quant_attr, Quantity) and quant_attr.dimension == dim):
- if not isinstance(quant_attr, Dimension): rv.append(i)
-
+ dim = getattr(u, quantity, None)
+ if dim is None:
+ for i, a in IA:
+ if quantity in i:
+ rv.add(i)
+ rv.update(find_unit(i))
+ else:
+ rv.update([i for i, _ in IA if quantity in i])
+ if isinstance(dim, Quantity):
+ dim = dim.dimension
+ for i, quant_attr in IA:
+ if quantity in i or (isinstance(quant_attr, Quantity)
+ and quant_attr.dimension == dim):
+ if not isinstance(quant_attr, Dimension):
+ rv.add(i)
else:
- for i in sorted(dir(u)):
- other = getattr(u, i)
- if not isinstance(other, Quantity):
- continue
+ for i, other in IA:
if isinstance(quantity, Quantity):
- if quantity.dimension == other.dimension:
- rv.append(str(i))
+ if isinstance(other, Quantity) and quantity.dimension == other.dimension:
+ rv.add(i)
elif isinstance(quantity, Dimension):
- if other.dimension == quantity:
- rv.append(str(i))
- elif other.dimension == Dimension(Quantity.get_dimensional_expr(quantity)):
- rv.append(str(i))
- return sorted(rv, key=len)
+ if isinstance(other, Quantity) and other.dimension == quantity:
+ rv.add(i)
+ elif isinstance(other, Quantity) and other.dimension == Dimension(Quantity.get_dimensional_expr(quantity)):
+ rv.add(i)
+ if not rv:
+ return []
+ return list(list(zip(*sorted(zip([len(i) for i in rv], rv))))[1])
# NOTE: the old units module had additional variables:
# 'density', 'illuminance', 'resistance'.
diff --git a/sympy/physics/units/tests/test_quantities.py b/sympy/physics/units/tests/test_quantities.py
index fd9f6b0..94bda98 100644
--- a/sympy/physics/units/tests/test_quantities.py
+++ b/sympy/physics/units/tests/test_quantities.py
@@ -221,6 +221,8 @@ def test_find_unit():
assert find_unit('coulomb') == ['C', 'coulomb', 'coulombs',
'planck_charge', 'coulomb_constant']
assert find_unit(coulomb) == ['C', 'coulomb', 'coulombs', 'planck_charge']
+ assert find_unit('char') == ['C', 'charge', 'coulomb', 'coulombs', 'planck_charge']
+ assert find_unit('charge') == ['C', 'charge', 'coulomb', 'coulombs', 'planck_charge']
assert find_unit(charge) == ['C', 'coulomb', 'coulombs', 'planck_charge']
assert find_unit(inch) == [
'm', 'au', 'cm', 'dm', 'ft', 'km', 'ly', 'mi', 'mm', 'nm', 'pm', 'um',
@@ -237,7 +239,7 @@ def test_find_unit():
'l', 'cl', 'dl', 'ml', 'liter', 'quart', 'liters', 'quarts',
'deciliter', 'centiliter', 'deciliters', 'milliliter',
'centiliters', 'milliliters', 'planck_volume']
- assert find_unit('voltage') == ['V', 'v', 'volt', 'volts', 'planck_voltage']
+ assert find_unit('voltage') == ['V', 'v', 'volt', 'volts', 'voltage', 'planck_voltage']
def test_Quantity_derivative(): |
Changes made as suggested by @smichr
@smichr, Thank's for the valuable input. I have committed these suggested changes. I had one question regarding the current implementation; when |
I still think |
Can you give an example of what the output is that you would like it not to be? |
probably not...we could just special case that and return []. |
Now returns an empty list
@ylemkimon , why don't you think Dimensions should be included in the return result? As it is now, the original intent of the docstring should be restored to indicate that dimensions may be returned, too. Otherwise, if there is a good reason not to, then only the code needs to be modified and the docstring left alone. |
Would it make sense to return dimensions if the quantity is ''? --- a/sympy/physics/units/__init__.py
+++ b/sympy/physics/units/__init__.py
@@ -249,34 +249,27 @@ def find_unit(quantity):
"""
import sympy.physics.units as u
+ IQ = [(i, getattr(u, i)) for i in dir(u)]
+ if not quantity:
+ return sorted([(i, q) for i, q in IQ if isinstance(q, Dimension)])
+ IQ = [(i, q) for i, q in IQ if isinstance(q, Quantity)]
rv = set()
- IA = [(i, getattr(u, i)) for i in dir(u)]
- IA = [(i, a) for i, a in IA if isinstance(a, (Quantity, Dimension))]
if isinstance(quantity, string_types):
+ rv.update([i for i, _ in IQ if quantity in i])
dim = getattr(u, quantity, None)
- if dim is None:
- for i, a in IA:
- if quantity in i:
- rv.add(i)
- rv.update(find_unit(i))
- else:
- rv.update([i for i, _ in IA if quantity in i])
+ if dim is not None:
if isinstance(dim, Quantity):
dim = dim.dimension
- for i, quant_attr in IA:
- if quantity in i or (isinstance(quant_attr, Quantity)
- and quant_attr.dimension == dim):
- if not isinstance(quant_attr, Dimension):
- rv.add(i)
- else:
- for i, other in IA:
- if isinstance(quantity, Quantity):
- if isinstance(other, Quantity) and quantity.dimension == other.dimension:
+ for i, quant in IQ:
+ if dim == quant.dimension:
rv.add(i)
- elif isinstance(quantity, Dimension):
- if isinstance(other, Quantity) and other.dimension == quantity:
- rv.add(i)
- elif isinstance(other, Quantity) and other.dimension == Dimension(Quantity.get_dimensional_expr(quantity)):
+ else:
+ if isinstance(quantity, Quantity):
+ dim = quantity.dimension
+ else:
+ dim = Dimension(Quantity.get_dimensional_expr(quantity))
+ for i, quant in IQ:
+ if dim == quant.dimension:
rv.add(i)
if not rv:
return [] |
@smichr |
@ylemkimon , here's an example of how someone might use the null find to get the dimensions and then use them for a further search:
...not sure why |
@ylemkimon, will it be fine to rename the function as find_unit_dimension(). I think this will the eliminate the possibility of any unnecessary confusion in future. |
Or a function named |
@Upabjojr Is this PR having a chance to be merged? |
The PR seems to be a valid one. @Shikhar1998 Would you still like to continue your work on this? Thanks for your contributions. |
Re-written sub-routine for string quantity arguments for find_unit.
References to other Issues or PRs
Fixes #13874
Brief description of what is fixed or changed
The input element is searched with all the elements of
units
dictionary and matched for theirdimensions
.Other comments
Moreover, in PR #13438 problem of multiple instances of
plank_length
andplank_charge
in resultant list forfind_unit('charge')
andfind_unit('length')
was raised.Example:
To counter this problem,
set
was used. However, there is no special need for it, as all thedefinitions
are inherently unique. This problem has also been eliminated.