IPython Notebook, Take 2

by Willie Wong

With the help of Fernando Perez, some aspects of my IPython Notebook code has been improved.


  1. Instead of using .show() and .fullshow() methods to display the content of the objects with dependence on a global toggle for MathJax, now we use the _repr_latex_ method (which hooks into IPython’s display functionality). This allows one to enter just m = math_expr([insert atoms here]) followed by m to display the content of the math expression.
  2. Whether the default display mentioned above shows the atom number labels is now configured via a boolean: one can play with it using the .labeled property
  3. To show the LaTeX code, we now use the .latex property
  4. Operations are no longer in place. Instead they return a new math_expr object. This has the advantage that if you can keep playing with the entry until you get it right, and then afterwords you can do m = m.replace(...). This makes it better for for interactive computations.
  5. By default, on object creation it will print its own content.

The code now looks like

from IPython.core.display import *

def add(x,y): return x+y
def MDPL(string): display(Math(string))
def comp_str(listofstrings): return reduce(add,listofstrings)

class math_expr(object):
   '''''Math Expression object'''''

   def __init__(self,atomslist, labeldefault = True, highlight = []):
      '''''init takes arg: list of atoms, each atom being a compilable chunck of LaTeX expression'''''
      self.listofatoms = atomslist
      self.labels = labeldefault

   def _repr_latex_(self):
      '''''Returns a latex expression of the object. Will be parsed by MathJax'''''
      latexstring = comp_str(self.labeledatoms) if self.labels else comp_str(self.listofatoms)
      return "$" + latexstring + "$"

   def labeled(self):
      '''''Tells you whether labelling is turned on by default'''''
      return self.labels

   def label(self,boolean):
      '''''Sets the default labelling'''''
      self.labels = boolean

   def latex(self):
      '''''Accesses the LaTeX code snip for the expression'''''

   def __labelatoms(self):
      '''''Label atoms by adding underbraces'''''
      self.labeledatoms = [ "\underbrace{" + self.listofatoms[i] + "}_{" + str(i) + "}" for i in range(len(self.listofatoms)) ]

   def replace(self,pos,newstr):
      '''''Replaces an atom with another atom'''''
      newstrings = list(self.listofatoms)
      newstrings[pos] = newstr
      return math_expr(newstrings,self.labels,[pos])

   def merge(self,positions):
      '''''Merges atoms: the input is a list of positions. The new atom is placed at the position of the foremost of the positions'''''
      newstrings = list(self.listofatoms)
      temp = [ newstrings[i] for i in positions ]
      for i in positions: del newstrings[i]
      return math_expr(newstrings,self.labels,[positions[-1]])

   def split(self,pos,newatoms):
      '''''Splits atoms: replaces an atom in place with multiple sub atoms'''''
      newstrings = list(self.listofatoms)
      del newstrings[pos]
      templen = len(newatoms)
      while len(newatoms) > 0:
      return math_expr(newstrings,self.labels,range(pos,pos+templen))

   def cancel(self,positions):
      '''''Cancels a bunch of terms: input a list of positions'''''
      newstrings = list(self.listofatoms)
      for i in positions: del newstrings[i]
      return math_expr(newstrings,self.labels)

   def move(self,posini,posfin):
      '''''Move atom at posini to posfin, pushing all others back'''''
      newstrings = list(self.listofatoms)
      temp = newstrings.pop(posini)
      newstrings.insert(posfin if posfin < posini else posfin-1, temp)
      return math_expr(newstrings,self.labels,[posfin if posfin < posini else posfin - 1])

   def __colouratoms(self,positions,labelled=False):
      '''''Returns the list of atoms, but with selected terms coloured'''''
      temp = list(self.listofatoms)
      if labelled:
         temp = list(self.labeledatoms)
      for i in positions: temp[i] = "\color{red}{"+temp[i]+"}"
      return temp