connorferster / handcalcs Goto Github PK
View Code? Open in Web Editor NEWPython library for converting Python calculations into rendered latex.
License: Apache License 2.0
Python library for converting Python calculations into rendered latex.
License: Apache License 2.0
As seen below, the equation is correctly calculated by Python, but the latex syntax does not produce a valid output.
Changing 38000 to 1 works.
How to reproduce
Expected Behaviour
The following works with just a change of one integer value.
There is a separate issue with the brackets of above examples, but I have raised this separately.
Guess the version of github is newer than the version of pip source.
Please upgrade the version to pip
Would it be a good idea to have these magic command shortcuts to avoid having the mandatory comment?
%%renderp
(parameter)%%renderl
(long)%%renders
(short)%%rendersym
(only symbolic)In an #output or #parameters cell, allow the ability to render a dictionary of values as though it was a cell populated with those values or parameters.
This would allow importing data from other sources and having it seamlessly integrate into a handcalcs workflow.
For a variable with units using forallpeople library, handcalcs does not render intermediate steps when you then call the .value method on that variable.
How to reproduce
forallpeople.environment('structural',top_level = True)
M.value
prevents the substitution steps being shown:Expected Behaviour
Everything should display normally with intermediate steps. See what happens below when you remove the .value
.
There is another issue evident with brackets in above image, but I will raise this separately.
I'm trying to do some value renders so that I can display them as currency. Is there an easy way to do this?
Would I need to implement a "currency" class? or could I somehow get units of "Dollars" from something like forallpeople?
Description of Problem
When using the @handcalc decorator preceding a function, and then calling that function, the variables that are in the global namespace (but not passed into the function call) are not displayed in the back-substitution step. Instead the variable name is displayed also with subscripting on first letter after underscore only.
The output of the result is correct, so the global variables are visible to Python, but not to handcalcs module.
Expected Behaviour
Both local and global variable values are shown in the back-substitution stage.
Setup
MacOS High Sierra
Anaconda & Python 3.8
handcalcs 1.0.3
Hello!
I have been loving your project, I think it has saved me countless hours in my reports already, thank you very much for producing it.
My question is:
Whenever the units come out automatically, ie they are chosen by your engine, sometimes they are the wrong choice.
What I mean by this, is that sometimes the numbers are too small as a result, and all that is displayed for an answer is:
0.000000 [some unit]
Is there a way to 'force' the end unit choice to be of a lower denomination, making the actual number visible?
Here is some code which will reproduce the issue:
`import handcalcs.render
import math
import forallpeople as si
import numpy as np
si.environment('default')
%%render
L = 470 * 10**(-3) * si.m
OC = 0.013699 * 10**(-3) * si.m / (10**(-3)si.kg)
R = 0.005435 * 10*(-3) * si.m / (10**(-3)*si.kg)
E = 69 *10**9 *si.Pa
%%render
I_x = (L**3)/(3E(OC + R))`
This outputs: I_x = 0.000 kg^2 s^2
As an aside, I am not even getting the units I want, which should be: (length)^4 ie. (mm)^4.
Thanks!
Hope this question can help some others too.
Describe Issue
Percentage (%) signs inside a comment are interpreted as a latex comment latex.wikia.org and cause an error when used in Streamlit. Behaviour is correct when using cell magic inside Jupyter.
Solution would be to escape any instances of % with a preceding backslash (\) to avoid this issue.
I have posted a related issue #61 about using backslashes in comment blocks
How to reproduce
Create a function using % in comment field and run it:
@handcalc(override="long")
def overlap1(q,p):
Ov = q / p #Overlap, where 50% <= Ov <= 100%
return Ov
#Test Overlap using Streamlit latex command:
Ov_lat, Ov_val = overlap1(125,270)
st.latex(Ov_lat)
st.write(Ov_lat)
Generated Latex - you can see the GitHub latex syntax highlighter considers the first % as the start of the comment
\begin{aligned} \mathrm{Ov} &= \frac{ q }{ p } \&= \frac{ 125 }{ 270 } \&= 0.463 \; \;\textrm{(Overlap, where 50% <= Ov <= 100%)}\ \end{aligned}
Resulting error
KaTeX parse error: Expected '}', got '\end' at position 135: …Ov <= 100%)}\\ \̲e̲n̲d̲{aligned}
Hardware
MacOS
handcalcs 1.1.3
streamlit 0.67.1
%%render
#Long
wall_North
wall_East = (6.63*m) * h_floors * 400*mm * (24.5*kN/(m**3))
wall_South_elev = (2.78*m) * h_floors * (250*mm) * (24.5*kN/(m**3))
This caused a problem.
Depending on the country where someone lives, the symbol used for the decimal separator and thousands separator changes.
This would be a nice addition to increase the similarity between the rendered output of handcalcs and manual calculation. Is it possible to allow the user to change the decimal and thousands separator in handcalcs?
a = 1e-2
currently causes an error.
To get around this, this works:
a = (1e-2)
But it would be good if parentheses are not needed.
I would like to print the unknown sympy variables just like the know variables, in three columns thereby saving vertical space.
I tried the different combinations, because according the release notes there is now support to use other override tags with the sympy override tag.
#%%
import handcalcs.render
from sympy import *
#%%
n_1, n_tot = symbols('n_1, n_tot')
n_2 = symbols('n_2')
#%%
%%render sympy params
# Params equal to itself
n_1
n_tot
n_2
#%%
%%render sympy symbolic
# Params stacked vertically
n_1
n_tot
n_2
#%%
%%render sympy symbolic params
# I would expect this would result in "n_1 n_tot n_2" on one row
n_1
n_tot
n_2
I'm looking for the same result as render sympy symbolic
but horizontal, instead of vertical.
When entering an arithmetic equation without a variable assignment, handcalcs creates an error.
Error Message
KeyError Traceback (most recent call last)
<ipython-input-8-306984f7e9b7> in <module>
----> 1 get_ipython().run_cell_magic('render', '', '2+2\n')
~/opt/miniconda3/envs/mywork1/lib/python3.8/site-packages/IPython/core/interactiveshell.py in run_cell_magic(self, magic_name, line, cell)
2369 with self.builtin_trap:
2370 args = (magic_arg_s, cell)
-> 2371 result = fn(*args, **kwargs)
2372 return result
2373
~/opt/miniconda3/envs/mywork1/lib/python3.8/site-packages/handcalcs/render.py in render(line, cell)
102 # Do the handcalc conversion
103 renderer = hand.LatexRenderer(cell, user_ns_postrun, line_args)
--> 104 latex_code = renderer.render()
105
106 # Display, but not as an "output"
~/opt/miniconda3/envs/mywork1/lib/python3.8/site-packages/handcalcs/handcalcs.py in render(self)
222 self.precision,
223 LatexRenderer.dec_sep,
--> 224 )
225
226
~/opt/miniconda3/envs/mywork1/lib/python3.8/site-packages/handcalcs/handcalcs.py in latex(raw_python_source, calculated_results, override, precision, dec_sep)
244
245
--> 246 def create_param_cell(
247 raw_source: str, calculated_result: dict, precision: int
248 ) -> ParameterCell:
~/opt/miniconda3/envs/mywork1/lib/python3.8/site-packages/handcalcs/handcalcs.py in categorize_lines(cell)
427 incoming.append(categorized_w_result_appended)
428 cell.lines = incoming
--> 429 return cell
430
431
~/opt/miniconda3/envs/mywork1/lib/python3.8/site-packages/handcalcs/handcalcs.py in categorize_line(line, calculated_results, override)
515 categorized_line = create_conditional_line(
516 line, calculated_results, override, comment
--> 517 )
518
519 elif "=" in line:
~/opt/miniconda3/envs/mywork1/lib/python3.8/site-packages/handcalcs/handcalcs.py in split_parameter_line(line, calculated_results)
1463 param_line = deque([param, "=", calculated_results[param]])
1464 return param_line
-> 1465
1466
1467 def format_strings(string: str, comment: bool) -> deque:
KeyError: '2+2')```
It would be convenient to have a #sympy cell that allowed for a handcalcs-style rendering the of sympy expressions. Handcalcs supports only numerical calculation. Adding support for sympy expressions could allow a user to seamlessly go between algebraic manipulation to using numeric calculations.
Didn't want to make a full fork of this (though GitHub did ¬¬)
Line 186 in the README.md file should probably read:
``handcalcs`` was designed to be used with the units package, [forallpeople](https://github.com/connorferster/forallpeople) (and [forallpeople](https://github.com/connorferster/forallpeople) was designed to be compatible with
handcalcs). However, it has been recently reported that [pint](https://pint.readthedocs.org) can work to good effect, also.
Current version is missing https://
before pint.readthedocs.org
, so it tries to redirect inside the repo.
That's all :)
Depending on the country where someone lives, the symbol used for the multiplication symbol is different. Some use a dot operator ⋅
or an asterisk ∗
for example.
Is it possible to allow the user to change the multiplication symbol in handcalcs? This is similar to #39.
It would perhaps be useful to create the functionality of render outside of Jupyter in the form of a function decorator:
from handcalcs import handrender # or something
@handrender
def my_calcs(a, b, c, ...):
d = a+ b
f = d**c + a*b
return locals()
The decorator would inspect
the source of my_calcs
() and pass the source and returned locals dict to the handcalcs.latex() function and would return a tuple of the rendered Latex code, as a str, along with the last calculated variable of the function (i.e. the "result" value), which would be the last entry to the dictionary.
This would allow the rendered Latex code to be used in any other python application that can render Latex in the browser or otherwise (e.g. Streamlit)
As brought up in #14
Hi thanks for this library, it's nifty.
My idea is simple: it would be great to add rendering of reusable mathematical functions.
This could render "normal" functions (perhaps required to be in a single line like example below):
def tau(sigma, phi, c): return sigma * tan(phi) + c # mohr-coulomb failure criterion
Or it could use a statement assigning a name to a lambda function, which by its very nature only allows a single line:
tau = lambda (sigma, phi, c): sigma * tan(phi) + c # mohr-coulomb failure criterion
Another option would be to cheat, and (ab)use valid python set item (square bracket) syntax as shown below (leveraging sympy symbols):
from sympy import symbols, tan
sigma_v, phi_0, c = symbols("sigma_v phi_0 c")
from handcalcs import mathfunc
tau = mathfunc()
tau[sigma, phi, c] = sigma * tan(phi) + c # mohr-coulomb failure criterion
I really like the look of the last idea. No return
, no lambda
, just nice looking mathematical statement. But it's obviously fraught with possible implementation problems.
You can already sort of do this using sympy:
# cell 1
from handcalcs import render
from sympy import symbols, tan
tau, sigma_v, phi_0, c = symbols("""tau sigma_v phi_0 c""")
# cell 2
%%render
tau = (sigma_v * tan(phi_0) + c) # mohr-coulomb failure criterion
...but the syntax for calling them is blech:
deg = pi / 180
tau.evalf(subs = {sigma_v:100, c:0, phi_0:30 * deg})
What do you think?
render long
and render params
doesn't function correctly when using a conditional (if) statement. Instead it shows if = If. render short
works fine.
Also, if the else:
clause is valid, the latex does not show any indication that a conditional statement was run. Some potential options could say something like: Since f_cmi ≰ 40 * MPa: E_1d = 2400^1.5 ...
The latex symbol used could be \nless
or similar. This may be harder said than done, if handcalcs only reads each conditional line independently.
Example not working using %%render long
System
Mac OS High Sierra
Anaconda Python 3.8
Jupyter Notebook
Hi there,
first of all, congratulations!! This is an amazing module!!
I discovered it yesterday and it works really well.
Sometimes I need to use small numbers and then the result presented is 0.
There is any option to obtain scientific notation for results smaller than 0.1??
Example:
%%render
l = 0.05 # Cube size [m]
V = l**3 # Cube volume [m^3]
Thanks in advance!!
When leaving a blank line within your calculation cell code, a TypeError is triggered because a blank line is currently unclassified and results in a None type being passed in to the single dispatch functions.
Adding a None type function to the first single dispatch functions could solve this.
My question concerns this line in the rounding conversion :
handcalcs/handcalcs/handcalcs.py
Line 1504 in e1b935a
where if the object has a __len__
attribute, is not a str
nor a dict
, but IF the rounding or the iteration fails and raises an exception (which is what happens in my case for reasons), the item is never added to the outgoing deque, hence, never rendered.
Is this behavior where the item is never added normal ?
PS : In my case, simply adding outgoing.append(latex_repr(rounded))
solves my problem.
This causes a problem:
a = (((1 / (2*m.value*(3**2*2+4*3)))+1)*2+1)*2
A string variable containing an underscore renders characters subsequent to the underscore in subscripts.
It seems logical that the subscripting behaviour only occurs on the variable names, and not its value.
How to reproduce
Expected Behaviour
Render the string without subscripting
John_Smith
Setup
handcalcs 1.1.3
MacOS High Sierra
Jupyter Notebooks
Looking over your pyparsing parser for handcalcs, I'm amazed you can do so much with such a simple parser!
One thing I thought you might want to revisit is the operator
expression, which currently reads:
operator = pp.Word("+-*/^%<>=~!,")
As you've probably learned, the Word
class will read as many characters as it can at a time, so this expression will gladly accept any of these as an 'operator':
++++
****
-+-+-+-
+-*/^%<>=~!,+-*/^%<>=~!,+-*/^%<>=~!,+-*/^%<>=~!,+-*/^%<>=~!,
This certainly has the advantage of keeping your maintenance down on this parser (since you might accept any combination of characters such as <=
, <>
, ~=
, etc.), but may give your users some unusual or unexpected results.
If you do feel like tightening this up, you can use pp.oneOf
, with a single string of space-delimited symbols, something like:
operator = pp.oneOf("+ - * / ^ % < > = ~ ! , >= <= <> != == ~= ** // += -= *= /=")
oneOf
will reorder these internally so that <
coming before <=
won't prevent <=
from parsing (as you might have problems with in a corresponding regex).
Of course, the downside is that if the need for <<
or <<<
comes along, if you use oneOf
you'll need to manually add them, whereas currently, the parser will just take care of things for you. So I simply put this out there for your consideration.
On a related note, will Unicode operators be something you might need, such as ∩ ∪ × ÷ ≠ ≤ ≥ ∈ ∉
? I found them very nice to add to my own eval()
replacement drop-in, plusminus
. You could just add them to this string as well. Though from my viewing of your demos, this may be handled through other mechanisms, and still rendered prettily.
Congrats on handcalcs! I've proudly added you to the "Who's Using Pyparsing?" section in the pyparsing wiki.
Cheers,
-- Paul
Hi,
This is more of a question than an issue.
import handcalcs.render
import random
from math import sin, cos, tan, atan, sqrt
from scipy.constants import epsilon_0, pi
q_1 = 0.4
q_2 = 0.8
F = 0.2
%%render
r^2 = ((1 / (4 * pi * epsilon_0 * F)) * (q_1 * q_2))
raises an error:
File "<ipython-input-26-b8ebc75e0a86>", line 1
r^2 = ((1 / (4 * pi * epsilon_0 * F)) * (q_1 * q_2))
^
SyntaxError: cannot assign to operator
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-26-b9f68fb55697> in <module>
----> 1 get_ipython().run_cell_magic('render', '', 'r^2 = ((1 / (4 * pi * epsilon_0 * F)) * (q_1 * q_2))\n')
~/opt/anaconda3/envs/handcalc/lib/python3.8/site-packages/IPython/core/interactiveshell.py in run_cell_magic(self, magic_name, line, cell)
2379 with self.builtin_trap:
2380 args = (magic_arg_s, cell)
-> 2381 result = fn(*args, **kwargs)
2382 return result
2383
~/Documents/handcalcs/handcalcs/render.py in render(line, cell)
48 # Do the handcalc conversion
49 renderer = hand.LatexRenderer(cell, var_dict)
---> 50 latex_code = renderer.render()
51
52 # Display, but not as an "output"
~/Documents/handcalcs/handcalcs/handcalcs.py in render(self)
151
152 def render(self):
--> 153 return latex(self.source, self.results, self.precision)
154
155
~/Documents/handcalcs/handcalcs/handcalcs.py in latex(raw_python_source, calculated_results, precision)
161 source = raw_python_source
162 cell = categorize_raw_cell(source, calculated_results)
--> 163 cell = categorize_lines(cell)
164 cell = convert_cell(cell)
165 cell = format_cell(cell)
~/Documents/handcalcs/handcalcs/handcalcs.py in categorize_lines(cell)
265 elif isinstance(cell, SymbolicCell):
266 override = "symbolic"
--> 267 categorized = categorize_line(line, calculated_results, override)
268 categorized_w_result_appended = add_result_values_to_line(
269 categorized, calculated_results
~/Documents/handcalcs/handcalcs/handcalcs.py in categorize_line(line, calculated_results, override)
330 elif test_for_parameter_line(line):
331 categorized_line = ParameterLine(
--> 332 split_parameter_line(line, calculated_results), comment, ""
333 )
334
~/Documents/handcalcs/handcalcs/handcalcs.py in split_parameter_line(line, calculated_results)
1161 """
1162 param = line.replace(" ", "").split("=")[0]
-> 1163 param_line = deque([param, "=", calculated_results[param]])
1164 return param_line
1165
KeyError: 'r^2'
Is there any possible way, in which I can print the expression?
I'm trying to use handcalcs and sympy to replace solving equations by hand. I also found this answer on Stackoverflow.
This way of declaring variables doesn't work and is my preferred method to declare variables (docs).
n_1, n_2, n_tot = symbols('n_1, n_2, n_tot') # This doesn't work and is the preferred method
#%%
import handcalcs.render
from sympy import *
# init_session()
# init_printing(use_latex='mathjax')
#%% md
### Goal
* Solve this equation for `n_2`
* `n_tot = n_1 + n_2 - n_1*n_2`
* with:
* `n_tot = 0.6`
* `n_1 = 0.4`
### Steps
1. List all known and unknown variables
2. Write the general formula
3. Transform the formula to the unknown variable on the left side
4. Subsitute the known variables in the tranformed equation
5. Calculate the value
#%%
%%render
# parameters
# 1. List all parameters (copy LHS between the quotes when using symbols())
# Known parameters
n_1 = Symbol('n_1')
n_tot = Symbol('n_tot')
v = {n_tot: 0.6, n_1 : 0.4}
# Unknown parameter
n_2 = Symbol('n_2')
#%%
# %%render # Can this be rendered with handcalc?
# 2. Write the general formula
my_expr = Eq(n_tot, n_1+n_2-n_1*n_2)
# Should render: n_tot = n_1 + n_2 - n_1*n_2
#%%
# %%render # Can this be rendered with handcalc?
# 3. Transform the formula to the unknown on the left side
my_expr_tranf = solve(my_expr, n_2) # Should be converted to latex
my_expr_tranf
# Should render: n_2 = (n_tot-n_1)/(1-n_1) # or similar
#%%
# %%render # Can this be rendered with handcalc?
# 4. Subsitute the known variables in the tranformed equation
sol = my_expr.subs(v)
sol
# Should render: n_2 = (0.6-0.4)/(1-0.4) # or similar
#%%
# %%render # Can this be rendered with handcalc?
# 5. Calculate the value
solve(sol, n_2)
# Should render: n_2 = 1/3
Incorrect rendering of brackets when you raise the first number to the power of x after an open bracket. This leads to expression being mathematically incorrect.
How to reproduce
Expected Behaviour
Correct brackets are shown. See example above with the power removed.
Setup
Mac OS High Sierra
Anaconda Python 3.8
I often find the formulas from paper are hard to read. Since we see much more formula than we write, would it be even cooler if we can use the same library to do the opposite? We input a latex or formula, and the library generates the python code for us to read or even do a quick experiment with the proposed formula. Is this possible?
Hi!
When I use the decorator handcalcs for function with a multiline doc string in a notebook, it causes an error.
It seems to be the function _func_source_to_cell in decorators.py which do not support a doc string with more than one line.
See below suggestion.
Current version:
def _func_source_to_cell(source: str):
"""
Returns a string that represents `source` but with no signature, doc string,
or return statement.
`source` is a string representing a function's complete source code.
"""
source_lines = source.split("\n")
acc = []
for line in source_lines:
doc_string = False
if not doc_string and '"""' in line:
doc_string = True
continue
elif doc_string and '"""' in line:
doc_string = False
continue
if (
"def" not in line
and not doc_string
and "return" not in line
and "@" not in line
):
acc.append(line)
return "\n".join(acc)
Suggestion:
def _func_source_to_cell(source: str):
"""
Returns a string that represents `source` but with no signature, doc string,
or return statement.
`source` is a string representing a function's complete source code.
"""
source_lines = source.split("\n")
acc = []
doc_string = False #moved
for line in source_lines:
if not doc_string and '"""' in line:
doc_string = True
continue
elif doc_string and '"""' in line:
doc_string = False
continue
elif doc_sting: #added
continue
if (
"def" not in line
and not doc_string
and "return" not in line
and "@" not in line
):
acc.append(line)
return "\n".join(acc)
(First time posting issues so might need to help me out)
I've been playing around with this package and have noticed that using the following:
import handcalcs.render
%%render
# Parameters
a, b, c = 1, 2, 3
throws a KeyError
exception. Am I missing something in the docs (I'm bad about skimming) or is this not yet implemented/possible?
(I also have jupyter notebook extensions installed and am thus not running the latest version of Jupyter Notebook)
Using Python 3.7.6 installed on a miniconda virtual environment.
Currently, all symbolic alphabetic characters are represented with italics. This is a default behaviour in Mathjax, Katex, and LaTex. It would be good if only single alphabetic character symbols were italic and multi-character symbols were upright.
Hi,
I've been using SageMath, but I figured out that your tool could possibly be a case for more portable notebooks, but I got a issue illustrated by the picture:
As you can see, the symbol for math.ceil
isn't rendering as expected (and math.log
either). Am I possibly doing something wrong or this is a issue?
That's it, and thanks for the great tool!
Leave a comment with a link to your project!
%%tex
a = 2
b = 3
c = 2*a + b/3
\[
\begin{aligned}
a &= 2\;
\\[10pt]
b &= 3\;
\\[10pt]
c &= 2 \cdot a + \frac{ b }{ 3 } = 2 \cdot 2 + \frac{ 3 }{ 3 } &= 5.0
\end{aligned}
\]
But in another way, Latex format string should be like below:
\begin{aligned}
a &= 2\;
\\[10pt]
b &= 3\;
\\[10pt]
c &= 2 \cdot a + \frac{ b }{ 3 } = 2 \cdot 2 + \frac{ 3 }{ 3 } &= 5.0
\end{aligned}
Hey Connor,
I'm having some trouble installing handcalcs through the pip interface, and was wondering if you could give me any suggestions.
Specifically, I was able to run the install command, but only the "init" module was downloaded. In "init" there is one function, "main", which is an equation from CSA S16-14.
If it helps, I'm using python through the Anaconda distribution.
Thanks, Christian
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.