Python library for converting Python calculations into rendered latex.

Overview

Coverage Status


Covert art by Joshua Hoiberg

handcalcs:
Python calculations in Jupyter,
as though you wrote them by hand.

handcalcs is a library to render Python calculation code automatically in Latex, but in a manner that mimics how one might format their calculation if it were written with a pencil: write the symbolic formula, followed by numeric substitutions, and then the result.

Because handcalcs shows the numeric substitution, the calculations become significantly easier to check and verify by hand.

Contents

Basic Demo

handcalcs demo 1

Installing

You can install using pip:

pip install handcalcs

Basic Usage 1: As a Jupyter cell magic (%%render)

handcalcs is intended to be used with either Jupyter Notebook or Jupyter Lab as a cell magic.

First, import the module and run the cell:

import handcalcs.render

Note: This will import both %%tex and %%render magics in the Jupyter Notebook.

Then, in any cell that you want to render with handcalcs, just use the render cell magic at the top of your cell:

%%render

For example:

%%render
a = 2
b = 3
c = 2*a + b/3

That is it!

Once rendered, you can then export your notebook as a PDF, provided you have a Latex environment installed on your system. If you are new to working with Latex and would like to install it on your system so you can use this functionality, please see the section Installing Tex, in the wiki.

You can also use the %%tex command to convert any Python code snippet into a valid LaTex. For Example:

First import handcalcs. We are also importing a few properties/functions from math package for the example below.

import handcalcs.render
from math import sqrt, pi

Now, you can use the %%tex magic!

%%tex
a = 2 / 3 * sqrt(pi)

This will produce a LaTeX output as follows.

\[
\begin{aligned}
a &= \frac{ 2 }{ 3 } \cdot \sqrt{ \pi } = \frac{ 2 }{ 3 } \cdot \sqrt{ 3.142 } &= 1.182
\end{aligned}
\]

Basic Usage 2: As a decorator on your functions, @handcalc()

Shout-out to @eriknw for developing innerscope and proactively integrating it into handcalcs. Thank you!

Start by importing the @handcalc() decorator:

from handcalcs.decorator import handcalc
@handcalc([override: str = "", precision: int = 3, left: str = "", right: str = "", jupyter_display: bool = False])

Returns a tuple consisting of (latex_code: str, locals: dict), where locals is a dictionary of all variables in the scope of the function namespace.

  • override is a str representing one of the acceptable override tags (see below)
  • precision is an int to alter the of decimal precision displayed
  • left and right are strings that can precede and follow the encoded Latex string, such as \\[ and \\] or $ and $
  • jupyter_display, when True, will return only the locals dictionary and instead will display the encoded Latex string rendering with display(Latex(latex_code)) from IPython.display. Will return an error if not used within

In your decorated function, everything between def my_calc(...) and a return statement (if any) is now like the code in a Jupyter cell, except it's a standard Python function.

Used in this way, you can use @handcalc() to dynamically generate Latex code for display in Jupyter and non-Jupypter Python environments (e.g. streamlit).

Parameters

Override tags

handcalcs makes certain assumptions about how you would like your calculation formatted and does not allow for a great deal of customization in this regard. However, there are currently four customizations you can make using # override tags as an argument after the %%render cell magic. Additionally, you can also specify the number of decimals of precision to display. You can only use one override tag per cell but you can combine an override tag with a precision setting.

Override tags can be used with both the Jupyter cell magic and the function decorator. To use a override tag with the decorator, you just supply it as an argument, e.g. @handcalc(override='params', precision=2)

I will compare a basic rendering of the quadratic formula (below) with the change achieved with each override tag.

Basic rendering:

Parameters


params:

handcalcs renders lines of code vertically, one after the other. However, when you are assigning variables, or displaying resulting variables, you may not want to waste all of that vertical space.

Using the params override tag, your list of parameters will instead render in three columns, thereby saving vertical space. Additionally, onsly the result will be shown, no calculations.

Params override example


Adjust precision:

The number of decimal places in a cell can be adjusted by providing an integer after %%render to indicate the decimal precision to be displayed. Can be combined with another override tag.

Precision override example


long and short:

To save vertical space, handcalcs attempts to figure out how long your calculation is and, if it is short enough, renders it out fully on one line.

If handcalcs's internal test deems the calculation as being too long to fit onto one line, it breaks it out into multiple lines.

Use the # long or # short override tags to override the length check and display the calculation in the "Long" format or the "Short" format for all calculations in the cell. e.g.

long: Spans multiple lines as though you had a long equation

Long override example

short: Forced to a single line as though you had a short equation

    # Format for "short" calculations (can fit on one line):
    c = 2*a + b/3 = 2*(2) + (3)/3 = 5

    # Format for "long" calculations (requires multi-line format)
    c = 2*a + b/3
      = 2*(2) + (3)/3
      = 5

Short override example


symbolic

The primary purpose of handcalcs is to render the full calculation with the numeric substitution. This allows for easy traceability and verification of the calculation.

However, there may be instances when it is preferred to simply display calculations symbolically. For example, you can use the symbolic tag to use handcalcs as a fast way to render Latex equations symbolically.

Alternatively, you may prefer to render out all of input parameters in one cell, your formulae symbolically in the following cell, and then all the final values in the last cell, skipping the numeric substitution process entirely.

Keep in mind that even if you use the symbolic tag with your calculations, you still need to declare those variables (by assigning values to them) ahead of time in order for your calculation to be valid Python.

Short override example


sympy

This is intended to be used only with sympy loaded. Sympy allows for symbolic manipulation, solving, and integration of algebraic expressions. Sympy will render its own objects in Latex without handcalcs.

If you are manipulating a sympy expression or sympy equation for the purpose of calculation, you can use handcalcs to handle the substitution and calculation of your resulting expression.

Note: Re-assigning your symbolic variables to numbers will clobber them as sympy variables. However, you are done with these now, right? So, it's no problem. If you need to work symbolically again, just re-run your notebook cells from the top.

Sympy demo


Units Packages Compatibility

handcalcs was designed to be used with the units package, forallpeople (and forallpeople was designed to be compatible with handcalcs). However, it has been recently reported that pint can work to good effect, also.

display variable demo

For potential compatibility with other units packages, please see the wiki.


Features

Quickly display the values of many variables

No more print statements needed. Just plop your variables onto a line and they will all be displayed.

display variable demo

Get Just the Latex Code, without the render

If you just want to generate the rendered Latex code directly to use in your own Latex files, you can use the %%tex cell magic instead:

%%tex
a = 2
b = 3
c = 2*a + b/3

Then you can just copy and paste the result into your own LaTeX document.

tex cell magic demo


Subscripts (and sub-subscripts, etc.)

Subscripts in variable names are automatically created when _ is used in the variable name. Sub-subscripts are nested for each separate _ used in series.

Subscripts demo


Greek symbols

Any variable name that contains a Greek letter (e.g. "pi", "upsilon", "eta", etc.) as a string or substring will be replaced by the appropriate Latex code to represent that Greek letter.

  • Using lower case letters as your variable name will make a lower case Greek letter.

  • Using a Capitalized Name for your variable will render it as an upper case Greek letter.

Greek symbols demo


Functions, built-in or custom

If you are using Python functions in your calculation, eg. min() or tan(), they will be replaced with Latex code to represent that function in Latex.

If you are creating your own functions, then they will be rendered in Latex as a custom operator.

If you are using a function with the name sqrt (whether your own custom implementation or from math.sqrt), then it will be rendered as the radical sign.

Functions


Rendered in-line Comments

Any comments placed after a line of calculation will be rendered as an inline comment in the Latex.

This makes it convenient to make notes along side your calculations to briefly explain where you may have acquired or derived a particular value.

Comments


Skip the substitution

Any calculation entirely wrapped in parentheses, (), will be rendered as just param = result, without the substitution.

This can be convient when you want to calculate a parameter on the fly and not have it be the focus of the calculation.

Skip the substitution


Conditional statements

Many calculations in the "real world" are dependent on context.

handcalcs allows for the inclusion of some simple conditional statements into its code in a way that makes it easier to understand the context of the calculation.

Conditional calculations

Note: Multiple "lines" of calculations can be used after the conditional expression provided that they are all on the same line and separated with ";". See Expected Behaviours for more context.


Numeric integration

You can use scipy.quad to perform numeric integration on a pre-defined function and have handcalcs perform a basic rendering of it.

This behaviour is triggered if you use a function with either integrate or quad in the name.

Numeric integration


"Prime" notation

Sometimes you need to write "prime" on your variables:

Prime Notation


PDF Printing in Jupyter

Note: With nbconvert v6.0, templates are handled in a different manner that is incompatible with the below method. Be sure to use nbconvert v5.6.1 to allow template installation and swapping.

Jupyter Notebooks/Lab are able to print notebooks to PDF through two methods. Both can produce great results with handcalcs:

  1. Export to HTML: Open the exported HTML page in your browser and print to PDF using your system's own PDF printer
    • Pros: No additional software required, you can include images copy-pasted into your Jupyter notebook, and you can change the scale of the printed PDF in your brower's print window.
    1. Cons: Page breaks can be less graceful on html output and you cannot otherwise customize the output further like you can with a .tex file
  2. Export to PDF (via Latex): Using your previously installed Latex distribution, Jupyter will first export your notebook to a .tex file and then render the file to PDF. This requires you to have a Latex distribution already installed on your system (Instructions: windows, mac os, ubuntu).
    • Pros: Page breaks tend to work better and you have the ability to customize your output further using the generated .tex file
    • Cons: Cannot easily rescale the PDF print (e.g. to ensure really long equations fit on the page) and you cannot include images copy/pasted into your Notebook. Images can be used but must be linked in with Markdown and the file must reside in the same directory as your Notebook.

PDF notebooks made with handcalcs tend to look better if the code input cells are suppressed. To make this convenient, handcalcs ships with two modified nbconvert template files that can be installed by running a function in Jupyter before exporting.

handcalcs.install_templates.install_html(swap_in:str = "", swap_out:str = "full.tpl", restore:bool = False)
handcalcs.install_templates.install_latex(swap_in:str = "", swap_out:str = "article.tplx", restore:bool = False)

swap_in: the name of the handcalcs template file you wish to install. When not provided, the function will print a list of available templates whose names are acceptable inputs for this argument.
swap_out: the name of the nbconvert template file you wish to replace (default file is nbconvert's default html or latex template, respectively)
restore: when set to True, the function will remove your previously installed template file and restore the default nbconvert template.

Design rationale

While there are methods for manually changing the template that nbconvert uses, this has to be performed on the command line as a separate conversion step. This default template override approach is not available from within the Jupyter GUI interface.

I have found that the easiest and most reliable way to quickly change the default export behaviour is to swap out and replace the default template files. By using this approach, you can export your notebooks directly from the Jupyter GUI menu options and have your notebooks look how you wish without fussing with multiple configuration settings that may or may not take.

Note

When handcalcs installs these templates, they make a semi-permanent change to your templates that will persist for all of your other notebooks that you print from with Jupyter, regardless of whether you are working with handcalcs or not. It does this because it is "physically" swapping out and replacing your nbconvert default template files for your local installation meaning it will persist past the end of your Jupyter session.

This change can be reverted at any time by using the restore = True argument. Additionally, the function will not let you repeatedly install the same template. If you wish to install another template, the function will prompt you to run the function with restore = True before attempting another installation.

In this way, handcalcs can fully manage these template installations for you. However, if you manually alter the file names of an installed handcalcs template in the nbconvert templates directory, there is no guarantee that your original template can be successfully restored.

Example of use

You can perform the same below process using either install_html or install_latex functions.

>>> from handcalcs.install_templates import install_html
>>> from handcalcs.install_templates import install_latex

>>> install_html() # Use with no arguments to discover available templates
Available templates:
 ['full_html_noinputs.tpl']
>>> install_html('full_html_noinputs.tpl') # Select the template you wish to install
/usr/Name/path/to/your/nbconvert/templates/dir/html/full.tpl
-is now-
/usr/Name/path/to/your/nbconvert/templates/dir/html/full_swapped.tpl

/usr/Name/path/to/your/handcalcs/templates/dir/html/full_html_noinputs.tpl
-is now-
/usr/Name/path/to/your/nbconvert/templates/dir/html/full.tpl

>>> install_html(restore = True) # To revert this change to your template files
/user/Name/path/to/your/nbconvert/templates/dir/html/full.tpl
-was restored from-
/user/Name/path/to/your/nbconvert/templates/dir/html/full_swapped.tpl

Expected Behaviours

handcalcs is intended to render arithmetical calculations written in Python code. It is not intended to render arbitrary Python into Latex.

Given that, handcalcs only renders a small subset of Python and there is a lot that will not work, especially anything that happens over multiple lines (e.g. function definitions, for loops, with statements, etc.).

handcalcs works by parsing individual lines of Python within a cell. It does not parse the cell as a whole. Therefore all statements to be rendered must be contained on a single line.

Accepted datatypes

handcalcs will make an attempt to render all datatypes. However, it cannot yet render all "collection" based data types, e.g. list and dict. If you are using a collection to hold argument functions, e.g. sum((23, 123, 45)), use a tuple to ensure it is rendered properly. Alternatively, you can use one-dimensional numpy arrays (vectors) with handcalcs.

Objects are rendered into Latex by two main approaches:

  1. If the object has a _repr_latex_() method defined, then that method is used.

    a) If the object has some alternate method for rendering itself into Latex code, e.g. .latex() or .to_latex(), that will be attempted as well.

    In order for the representation to be rendered properly, the object's Latex represention must use commands that are implemented with MathJax and/or Katex.

  2. If the object does not have a Latex method, then str() is used.

If you are using object types which have str methods that render as , then that's what the Latex interpreter will see and attempt to render.

Arithmetic operators

  • + renders as +
  • - renders as -
  • * renders as the "dot operator" (Latex: \cdot)
  • / always renders as a fraction
  • ** renders as superscripts
  • % renders as the "mod function" (Latex: \mod)

Currently // is not rendered but you can easily use math.floor as a function instead.

for loops and other iterations

Showing rendered iterations is not supported. The intention for use is that you perform your iterations in a cell that is not rendered and then, once the iteration has produced the desired resulting value, you render the result in a separate cell.

Gotchas

Because handcalcs is designed for use within the Jupyter environment, and because Jupyter cells can be run out of order, there exists the possibility of having a big mess of beautifully rendered but completely incorrect calculations if you re-use variable names throughout your notebook.

handcalcs uses the notebook's user namespace dictionary to look up values for all variables in the namespace. If your calculations are re-using variable names throughout the notebook, then the dictionary entry for that name may not be what you think it is when you run cells out of the order originally intended.

You can re-use variable names to good effect throughout a notebook, IFF the cells are run in the correct order (easier if this is just top to bottom).

On this note: if you are using handcalcs for any kind of reporting that may become a legal document (e.g. design engineering calculations), it is up to YOU to ensure that the results are what you expect them to be. handcalcs is free and open-source software and the author(s) are not responsible for incorrect calculations that result from its use.

That being said, the very purpose for the way handcalcs renders its math is to make it very easy to confirm and verify calculations by hand.

YouTube Tutorials

Getting Started with handcalcs (assumes zero Python knowledge)

https://www.youtube.com/watch?v=ZNFhLCWqA_g

Engineering Calculations: handcalcs-on-Jupyter vs. Excel

https://www.youtube.com/watch?v=n9Uzy3Eb-XI

Applications and Compatibility with OPP (Other People's Packages)

** Please see the wiki for applications of handcalcs in education and engineering, in addition to examples of using handcalcs with other Python libraries such streamlit and papermill.

Comments
  • Support for sympy

    Support for sympy

    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
    
    enhancement 
    opened by BTWS2 19
  • Allow function definition syntax

    Allow function definition syntax

    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?

    opened by Ricyteach 17
  • support for expressions on the LHS of an equation

    support for expressions on the LHS of an equation

    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?

    enhancement 
    opened by gxyd 15
  • nbformat version requirement

    nbformat version requirement

    hi @connorferster , first of all thanks for this wonderful package. We would like to use for some steel construction reporting.

    I see that one of the requirements is nbformat 5.6.1. Is there a specific reason why the current release of nbformat (6) is not compatible? And is there a way in which we can contribute to make it work?

    opened by RubendeBruin 11
  • param KaTeX ParseError in VS Code

    param KaTeX ParseError in VS Code

    import handcalcs.render
    
    %%render param
    a = 1
    
    %%render
    b = 1
    

    Error:

    ParseError: KaTeX parse error: Can't use function ']' in math mode at position 40: …;\end{aligned} \̲]̲

    VS Code

    image image

    PyCharm

    image

    Rendering in VS code works, but not with the param keyword.

    opened by BTWS2 7
  • Multiple division rendering

    Multiple division rendering

    handcalcs version: 1.1.3 As shown below, a should be rendered as c, otherwise it's the same rendering with b, which is totally different.

    %%render
    a = 4/2/2  # The answer will be 4 for the rendered equation
    b = 4/(2/2)
    c = (4/2)/2  # Should be correct rending
    d = 4/(2*2)  # Or this will be better
    

    image

    opened by yueyericardo 7
  • Other math symbols

    Other math symbols

    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:

    math_symbols

    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!

    opened by jm4rc05 7
  • Fix single line docstring parsing error

    Fix single line docstring parsing error

    What it fixes Related to Issue #66 this allows a single line doc-string to be used when using the python decorator. I read online that Regex needs to be compiled first, which can make it slower than the python string functions I adopted.

    Limitations I decided to keep it simple for my first pull request, in case the maintainer wants to style the code differently.

    • did not integrate multi-line definition statements #64
    • did not integrate docstrings with single quotes '''
    • did not run the test-suite, but did some manual testing below. I might learn how to do it myself for the next PR.

    Testing conducted

    • created a new environment in conda and pip install streamlit
    • Forked and cloned the handcalcs repo
    • python setup.py install --user inside the cloned handcalcs directory. see bottom as the installation didn't behave quite right
    • Run the following script including writing 2 identical functions except with a single vs. double line doc-string
    import streamlit as st
    import handcalcs
    
    @handcalcs.handcalc(override="long")
    def addition1(val1,val2):
        """doc string doc string 2"""
        c = val1/val2
        return c
    
    @handcalcs.handcalc(override="long")
    def addition2(val1,val2):
        """doc string
        doc string 2"""
        c = val1/val2
        return c
    
    latex1, vals1 = addition1(1,2)
    st.write("Single Line docstring")
    st.latex(latex1)
    
    latex2, vals2 = addition2(a,b)
    st.write("Multi Line docstring")
    st.latex(latex2)
    

    Output image

    Issues installing using python setup.py install When running the above script additionX(val1,val2) was generating the following console outputs (which I usually don't get when I pip install handcalcs). The latex appears to still work though.

    No match:  val1
    div:  /
    No match:  val2
    Return:  [True, False, True, False]
    op:  =
    No match:  val1
    div:  /
    No match:  val2
    Return:  [True, False, True, False]
    
    opened by michaellisitsa 6
  • Global namespace variables not back-subtituted within function using @handcalc

    Global namespace variables not back-subtituted within function using @handcalc

    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. 766F94D3-4A3A-4A01-9DA4-3B45EAE6FA55

    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

    opened by michaellisitsa 6
  • Simple sqrt error

    Simple sqrt error

    Hey, it's me again.

    Very simple problem:

    image

    As you can see, rendering only a sqrt equation isn't enought to symbolic do its magic. Instead, we got to add a random number to thing appear.

    opened by momba98 6
  • Is pyparsing operator expression too eager?

    Is pyparsing operator expression too eager?

    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

    opened by ptmcg 6
  • Rendering Issues

    Rendering Issues

    Hi! When I use %%render I don't get a proper rendering of the equations, just a pseudo-Latex code. Nevertheless, when I use %%render params the results are as expected.

    Thanks!

    Result Render Params Result Render

    opened by rsuarez1 2
  • Incorrect representation of scientific numbers with unit

    Incorrect representation of scientific numbers with unit "1/s" in mode "%%render sci_not"

    Versions:

    • handcalcs = 1.6.1
    • forallpeople = 2.6.3

    The error occurred during calculations of time rates in 1/s , 1/min, 1/hour.

    Without option "sci_not" the exponent is correct:

    %%render 
    # Time Rate in 1/sec
    tr = 1.23e-2 / si.s   # expected: 0.012 s⁻¹ --> OK
    

    With option "sci_not" the exponent is not correct in the result:

    %%render sci_not
    tr = 1.23e-2 / si.s   
    # expected: 1.230*10⁻² s⁻¹
    # actually: 1.230*10² s⁻¹  
    

    However, the output via print() is correct:

    print(f'tr={tr:.3e}')   
    # expected: 1.230e-02 s⁻¹ --> OK
    
    opened by Klummel69 0
  • Scientific notation with capital

    Scientific notation with capital "E" generates UnboundLocalError

    (Sorry Connor, I accidentally posted this Issue first in the foreallpeople project. I have closed it there)

    Used handcalcs Version: 1.6.1

    According to the python documentation, you can use a lowercase "e" or an uppercase "E" for scientific notation. See https://python-reference.readthedocs.io/en/latest/docs/float/scientific.html

    With handcalcs rendering sometimes only version with lowercase "e" works

    %%render
    a = 7.0 * 1.1e-2 
    # Output: a = ...= 0.077
    

    Handcalcs rendering with uppercase "E" and option param works:

    %%render param
    b = 7.0 * 1.1E-2
    # Output: b=0.077
    

    ​ Handcalcs rendering with uppercase "E" produces an "UnboundLocalError":

    %%render
    b = 7.0 * 1.1E-2 
    

    Output:

    ...
    UnboundLocalError                         Traceback (most recent call last)
    ~\AppData\Local\Temp/ipykernel_22072/742798243.py in <module>
    ----> 1 get_ipython().run_cell_magic('render', '', '# Handcalcs rendering with uppercase "E" produces an "UnboundLocalError"\nb = 7.0 * 1.1E-2 \n')
    
    ~\AppData\Local\Programs\Python\Python310\lib\site-packages\IPython\core\interactiveshell.py in run_cell_magic(self, magic_name, line, cell)
       2417             with self.builtin_trap:
       2418                 args = (magic_arg_s, cell)
    -> 2419                 result = fn(*args, **kwargs)
       2420             return result
       2421 
    
    ~\AppData\Local\Programs\Python\Python310\lib\site-packages\handcalcs\render.py in render(line, cell)
        104     # Do the handcalc conversion
        105     renderer = hand.LatexRenderer(cell, user_ns_postrun, line_args)
    --> 106     latex_code = renderer.render()
        107 
        108     # Display, but not as an "output"
    
    ~\AppData\Local\Programs\Python\Python310\lib\site-packages\handcalcs\handcalcs.py in render(self, config_options)
        183 
        184     def render(self, config_options: dict = global_config._config):
    --> 185         return latex(
        186             raw_python_source=self.source,
        187             calculated_results=self.results,
    ...
    -> 1251         return rendered_string
       1252 
       1253     # Procedure for atomic data items
    
    UnboundLocalError: local variable 'rendered_string' referenced before assignment
    
    opened by Klummel69 0
Releases(v1.6.0)
  • v1.6.0(Jul 4, 2022)

    handcalcs v1.6.0

    This is a major new release for handcalcs and introduces the global configuration feature. This allows users to have control over several options of how handcalcs works. The configuration options, with their default values, are as follow:

    • decimal_separator = "."
    • latex_block_start = "\\["
    • latex_block_end = "\\]"
    • math_environment_start = "aligned"
    • math_environment_end = "aligned"
    • line_break = "\\\\[10pt]"
    • use_scientific_notation = False
    • display_precision = 3
    • underscore_subscripts = True
    • greek_exclusions = []
    • param_columns = 3
    • preferred_string_formatter = "L"

    Config API

    import handcalcs.render
    
    handcalcs.set_option("display_precision", 4)
    handcalcs.set_option("param_columns", 5) 
    handcalcs.set_option("line_break", "\\\\[20pt]") 
    handcalcs.set_option("greek_exclusions", ["psi"]) # etc...
    

    These changes now affect all cells rendered in the current session. If you want to permanently update the config.json file with these changes (so handcalcs will always load up with these options), you can then call handcalcs.save_config() and the changes will be saved.

    The auto-complete in the handcalcs.set_option() function demonstrates which options are available and what values they take.

    Scientific notation

    Previously, handcalcs had a sketchy "guess" at determining if a value should be rendered in scientific notation to make it more readable. This behaviour has now been removed and requires the user to explicitly set whether scientific notation should be used. This can be set as a global option with handcalcs.set_option('use_scientific_notation', True) and it can also be toggled at the cell level with the new sci_not cell override command. If the global configuration is set to True, the toggle will turn scientific notation OFF in that cell. If the global configuration is set to False, the toggle will turn scientific notation ON in that cell.

    %%render sci_not 5
    a = 32.902939
    b = 0.0923
    

    image

    NBConvert Exporters

    Previously, there was a problem with the "noinput" exporters that come bundled with handcalcs. The "noinput" exporters would turn off inputs for both the regular exporters and the handcalcs exporters. This has been fixed. If you use Jupyter with different kernels, the controlling kernel for the exporters seems to be the "base" environment (if you use conda) so be sure to install v1.6.0 into your base environment to have access to the udpated exporters (which have a slightly new name, e.g. "HTML_NoInput" instead of "HTML_noinput", etc._)

    Bug Fixes

    This release also fixes several bugs. Some of these are from the new global config feature but others were independent bugs.

    Fixes:

    • #125 (Double ## comments not working in 'long' setting)
    • #121 (How to not use engineering notation)
    • #114 (Commented line with comment at the end)
    • #113 (Pint and siunitx)
    • #111 (Broken output when using forallpeople)
    • #101 #97 (Provide options for subscripts; optionally disable subscripting)
    • #90 ("psi" renders as a greek character)

    Coming soon

    • More tests for these new features (I am in a bit of a rush right now; lots on my plate)
    • Official documentation
    Source code(tar.gz)
    Source code(zip)
  • v.1.5.2(Jun 2, 2022)

  • v.1.5.0(May 21, 2022)

    This update is to include changes added via PR that were (regrettably) not previously published to PyPI.

    Big "Thank you!" to @daradib and @omouraenko for these:

    • #103 - Fix: Round non-iterable objects with len method
    • #105 - Fix: Copy exporter attribute instead of modifying parent class
    • #108 - Fix: Round sympy expressions and nested elements
    • #119 - Fix: Rendering issues in VS Code
    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(Jun 4, 2021)

    This release has been long awaited!

    The biggest news: handcalcs has FINALLY been updated to run on nbconvert v.6.0.0 and Jupyter Lab v3.0. Thanks to the contributions of @JimZwartveld, there are new exporters available from the Jupyter File -> Export as... menu to export HTML, Latex, and PDF with input cells hidden.

    Additionally, there is the ability to add text now in-between lines of math by using a ## (double pound sign) comment as per a request by @theengineer on #82. To summarize:

    • # comment on a new line will remain a Python comment and will not be rendered
    • ## comment on a new line will now display text in Latex
    • # comment at the end of a calculation will render the comment in Latex in parentheses as usual

    Next item on the handcalcs list, a proper documentation site on ReadtheDocs...

    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Dec 6, 2020)

    Handcalcs v1.2.0

    • Feature: NumericCalcLine: this is a new line type within handcalcs that recognizes when a calculation does not contain any variables, just numbers, and bypasses the substitution step since the "symbolic" step is just the numbers of the calculation. In other words, it avoids having a redundant substitution when you are not using variables.
    • Bug fix: Decorator would not recognized doc strings properly when they were coded onto a single line. This lead handcalcs to treat the entire function source as being within the doc string and nothing would be rendered. Thank you @michaellisitsa for your contribution and fixing this bug.
    • Bug fix: Nested log and other functions, under certain circumstances, would render incorrectly because the nested functions were not recognized.
    Source code(tar.gz)
    Source code(zip)
  • v.1.1.3(Oct 14, 2020)

    Thank you to @michaellisitsa who for posting issues for handcalcs recently. This release fixes the following:

    • Additional spaces around commas when used as decimal separators has now been removed
    • A bug involving white space underneath %%render params causing the alignment to be affected has been fixed (#52)
    • Inconsistencies with some parentheses around deeply nested calculations that start with function names have been fixed (#49)
    • Parentheses fully enclosing a calculation was not creating a "parameter line" (no substitution); this is now fixed (#51)
    • A bug involving complex numbers being inappropriately rendered in scientific notation

    Additionally, if anyone is using handcalcs in school or in their daily life, I have created issue #50 as a place people can showcase their rendered notebooks. I love seeing handcalcs being used so if you have something you would like to show off, please submit!

    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Oct 5, 2020)

    I am excited to release this version of handcalcs today especially...because it's my birthday :) I am excited because there are lots of new features that I think you will enjoy using and I am eager to tell you about them:

    Features

    • Localization: Use the %decimal_separator line magic to change the decimal separator character from . to any character you like (except _), e.g. %decimal_separator , to change the decimal separator to ,. This will affect every cell in the notebook until it is changed. To change the decimal separator with the decorator interface, use @handcalc(dec_sep = ","). Thank you @BTWS2 for this suggestion.
    • Complex numbers: They now render in handcalcs.
    • Additional function rendering: ceil, floor, and log functions are now swapped out for their proper notation (instead of as generic functions). Thank you @jm4rc05 for this suggestion.
    • Auto scientific notation: When working with floats that are small enough to get "cut-off" by the decimal precision setting when they are rendered out, they will automatically be rendered as scientific notation to the decimal precision specified to prevent loss of significant figures. Thank you @jlopez1979 for this suggestion.
    • Render global namespace vars with decorator: Previously, if you called a global variable from within a function decorated with @handcalc(), it would not render because it was not defined in the function local namespace. Now, they render with no problem. Thank you @michaellisitsa for bringing the issue up and @eriknw for providing the fix with innerscope (so excellent).
    • Render sympy symbols: Previously you could only render out expressions and equations. Now you can render out just your sympy symbols by using %%render sympy symbolic. Thank you to @BTWS2 for pushing me on this.
    • Use other override tags with sympy override tag: The sympy tag is special and does not need to be mutually exclusive from the other overrides. Now you can use sympy symbolic, sympy long, etc.

    Fixes

    • Doc string in decorated function: No longer causes errors. Thank you @KingSiz3 for reporting this error.
    • Conditional expressions with override tags: Works smoothly now. Thank you @michaellisitsa for reporting this error.

    I am excited to present v1.1.0 of handcalcs. Enjoy!

    Get the new release from the repo or on pypi: pip install handcalcs -U

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Sep 19, 2020)

    This is the first major release of handcalcs. Many new features have been added based feedback from users on Github and on /r/python. Thank you to everyone who has been using handcalcs. An extra big Thank You for everyone who has contributed by comments, issues, and PRs to help make it better.

    New Features

    • Breaking change: No more "comment tags", they are now called "override tags" and work as line arguments with the %%render magic, e.g. %%render params or %%render long, etc. When using the decorator interface, override is one of the arguments in the decorator, i.e. @handcalc(override = "params")
    • Display decimal precision can now be altered at the cell level, similar to an override tag, e.g. %%render 6 will display all floats up to six decimal places. This can work with an override tag, i.e. %%render long 8. If a float has only three digits, such as 1.275 only 1.275 will be displayed, not 1.275000, for example.
    • Sympy integration is now included. Sympy with handcalcs is intended to be used separately but together. For example, you want to solve a polynomial equation in Sympy and then want to substitute values. Solve the polynomial in Sympy first, then when you would normally use .subs(), instead use handcalcs to calculate and render. See the updated README.md on Github for a demonstration.
    • An entirely new parser means those extra brackets that were previously needed around fractions, e.g. (b**2)/(sqrt(a + b)) are now unnecessary. Use brackets as you normally would, i.e. b**2/sqrt(a+b.
    • Complex numbers are now supported (due to new parser)
    • Scientific notation is now supported (due to new parser)
    • Comparison operators in expressions now render properly
    • Extraneous parentheses that would show up in fractions in certain contexts no longer happen.
    • Function calls now only use parentheses when there is an expression or more than one argument being passed to the function
    • Variable names that are longer than one character (not including subscripts) are now rendered as upright and not italic
    • And more!

    Feel free to raise additional issues for features you do not see yet (there are two not fully implemented yet: ceil and floor symbols and automatically switching to scientific notation for small physics constants (however you can now change the decimal precision as a short term fix)).

    Thank you for all of the support everyone!

    Source code(tar.gz)
    Source code(zip)
Owner
Connor Ferster
Structural engineer-in-training in Vancouver, BC. Loves Python.
Connor Ferster
run-js Goal: The Easiest Way to Run JavaScript in Python

run-js Goal: The Easiest Way to Run JavaScript in Python features Stateless Async JS Functions No Intermediary Files Functional Programming CommonJS a

Daniel J. Dufour 9 Aug 16, 2022
A simple PID tuner and simulator.

PIDtuner-V0.1 PlantPy PID tuner version 0.1 Features Supports first order and ramp process models. Supports Proportional action on PV or error or a sp

3 Jun 23, 2022
TickerRain is an open-source web app that stores and analysis Reddit posts in a transparent and semi-interactive manner.

TickerRain is an open-source web app that stores and analysis Reddit posts in a transparent and semi-interactive manner

GonVas 180 Oct 08, 2022
An async API wrapper for Dress To Impress written in Python.

dti.py An async API wrapper for Dress To Impress written in Python. Some notes: For the time being, there are no front-facing docs for this beyond doc

Steve C 1 Dec 14, 2022
Simple Python Gemini browser with nice formatting

gg I wasn't satisfied with any of the other available Gemini clients, so I wrote my own. Requires Python 3.9 (maybe older, I haven't checked) and opti

Sarah Taube 2 Nov 21, 2021
LINUX-AOS (Automatic Optimization System)

LINUX-AOS (Automatic Optimization System)

1 Jul 12, 2022
python's memory-saving dictionary data structure

ConstDict python代替的Dict数据结构 若字典不会增加字段,只读/原字段修改 使用ConstDict可节省内存 Dict()内存主要消耗的地方: 1、Dict扩容机制,预留内存空间 2、Dict也是一个对象,内部会动态维护__dict__,增加slot类属性可以节省内容 节省内存大小

Grenter 1 Nov 03, 2021
A python script for compiling and executing .cc files

Debug And Run A python script for compiling and executing .cc files Example dbrun fname.cc [DEBUG MODE] Compiling fname.cc with C++17 ------------

1 May 28, 2022
The Ultimate Widevine Content Ripper (KEY Extract + Download + Decrypt) is REBORN

NARROWVINE-REBORN ** UPDATE 21.12.01 ** As expected Google patched its ChromeCDM Whitebox exploit by Satsuoni with a force-update on the ChromeCDM. Th

Vank0n 104 Dec 07, 2022
Demo code for "Logs in distributed systems" webinar

Hexlet Logs Demo Пререквизиты docker-compose python3 Учетка в DataDog Базовое понимание, что такое логи (можно почитать гайд

Anton Markelov 1 Dec 01, 2021
Install JetBrains Toolbox

ansible-role-jetbrains-toolbox Install JetBrains Toolbox Example Playbook This example is taken from molecule/default/converge.yml and is tested on ea

Antoine Mace 2 Feb 04, 2022
Unofficial Python implementation of the DNMF overlapping community detection algorithm

DNMF Unofficial Python implementation of the Discrete Non-negative Matrix Factorization (DNMF) overlapping community detection algorithm Paper Ye, Fan

Andrej Janchevski 3 Nov 30, 2021
FantasyBballHelper - Espn Fantasy Basketball Helper

ESPN FANTASY BASKETBALL HELPER The simple goal of this project is to allow fanta

1 Jan 18, 2022
An app to automatically take attendance by scanning students' bar coded ID card as they enter the classroom.

Auto Classroom Attendance This application may be run on a PC to automatically scan students' ID card using a generic bar code scanner and output the

1 Nov 10, 2021
Multtable is a collection of multiplication table generators in various languages.

Multtable Multtable is a collection of multiplication table generators in various languages. This project was created as a joke based on one of my bro

pollen__ 7 Mar 05, 2022
Serverless demo showing users how they can capture (and obfuscate) their Lambda payloads in Datadog APM

Serverless-capture-lambda-payload-demo Serverless demo showing users how they can capture (and obfuscate) their Lambda payloads in Datadog APM This wi

Datadog, Inc. 1 Nov 02, 2021
Python program to start your zoom meetings

zoomstarter Python programm to start your zoom meetings More about Initially this was a bash script for starting zoom meetings, but as i started devel

Viktor Cvetanovic 2 Nov 24, 2021
An execution framework for systematic strategies

WAGMI is an execution framework for systematic strategies. It is very much a work in progress, please don't expect it to work! Architecture The Django

Rich Atkinson 10 Mar 28, 2022
An app about keyboards, originating from the design of u/Sonnenschirm

keebapp-backend An app about keyboards, originating from the design of u/Sonnenschirm Setup Firstly, ensure that the environment for python is install

8 Sep 04, 2022
chiarose(XCR) based on chia(XCH) source code fork, open source public chain

chia-rosechain 一个无耻的小活动 | A shameless little event 如果您喜欢这个项目,请点击star 将赠送您520朵玫瑰,可以去 facebook 留下您的(xcr)地址,和github用户名。 If you like this project, please

ddou123 376 Dec 14, 2022