class ProveItMagic(Magics): "Magics that hold additional state" def __init__(self, shell, assignmentBehaviorModifier): # You must call the parent constructor super(ProveItMagic, self).__init__(shell) self.kind = None self.definitions = dict() self.keys = [] # the keys of the definitions in the order they appear self.lowerCaseNames = set() self.context = None self.ranFinish = False self.assignmentBehaviorModifier = assignmentBehaviorModifier assignmentBehaviorModifier.displayAssignments(ip) @line_magic def display_assignments(self, line): if line.strip() == 'off': self.assignmentBehaviorModifier.resetBehavior() else: self.assignmentBehaviorModifier.displayAssignments(self.shell) @line_magic def context(self, line): ''' Create the _common_, _axioms_ and _theorems_ notebooks for the current context (if they do not already exist). Show the table of contents for sub-contexts which may be edited. ''' import proveit import ipywidgets as widgets # create an '_init_.py' in the directory if there is not an existing one. if not os.path.isfile('__init__.py'): open('__init__.py', 'w').close() # create an empty __init__.py context = Context() proveit_path = os.path.split(proveit.__file__)[0] special_notebook_types = ('common', 'axioms', 'theorems', 'demonstrations') special_notebook_texts = ('common expressions', 'axioms', 'theorems', 'demonstrations') for special_notebook_type in special_notebook_types: notebook_name = '_%s_.ipynb' % special_notebook_type if not os.path.isfile(notebook_name): # notebook does not yet exist, create it from the template template_name = '_%s_template_.ipynb' % special_notebook_type with open(os.path.join(proveit_path, '..', template_name), 'r') as template: nb = template.read() nb = nb.replace('#CONTEXT#', context.name) # write the notebook file with open(notebook_name, 'w') as notebook_file: notebook_file.write(nb) context_interface = ContextInterface() if context_interface.mode == 'static': special_notebooks_html = '<table>\n' for special_notebook_type, special_notebook_text in zip( special_notebook_types, special_notebook_texts): special_notebooks_html += '<th><a class="ProveItLink" href="_%s_.ipynb">%s</a></th>\n' % ( special_notebook_type, special_notebook_text) special_notebooks_html += '</table>\n' if len(context_interface.subContextNames) > 0: special_notebooks_html += '<table>\n' for name in context_interface.subContextNames: description = context_interface.subContextDescriptions[ name] href = context_interface.subContextNotebook(name) special_notebooks_html += '<tr><th><a class="ProveItLink" href="%s">%s</a></th><td>%s</td></tr>\n' % ( href, name, description) special_notebooks_html += '</table>\n' display(HTML(special_notebooks_html)) else: special_notebook_links = [] full_width_layout = widgets.Layout(width='100%', padding='5px') for special_notebook_type, special_notebook_text in zip( special_notebook_types, special_notebook_texts): special_notebook_links.append( widgets.HTML( '<a class="ProveItLink" href="_%s_.ipynb">%s</a>' % (special_notebook_type, special_notebook_text), layout=full_width_layout)) special_notebook_links = widgets.HBox(special_notebook_links) sub_contexts_label = widgets.Label( 'List of sub-contexts:', layout=widgets.Layout(width='100%')) #sub_context_widgets = widgets.VBox(sub_context_widgets) add_context_widget = widgets.Text(value='', placeholder='Add sub-context...') def addSubContext(sender): context_interface.addSubContext(add_context_widget.value) add_context_widget.value = '' add_context_widget.on_submit(addSubContext) #layout = widgets.Layout(display='flex', flex_flow='column-reverse') #display(widgets.Button(description='Edit...', disabled=False, button_style='', tooltip='Edit the sub-contents list', layout=layout)) #layout = widgets.Layout(float='bottom') display( widgets.VBox([ special_notebook_links, sub_contexts_label, context_interface.widget, add_context_widget ])) def begin_axioms(self): # context based upon current working directory self.context = Context() if len(self.definitions) > 0 or self.kind is not None: if self.kind != 'axioms': raise ProveItMagicFailure( "Run %%begin_axioms in a separate notebook from %%begin_%s." % self.kind) print "WARNING: Re-running %begin_axioms does not reset previously defined axioms." print " It is suggested that you restart and run all cells after editing axioms." print "Defining axioms for context '" + self.context.name + "'" print "Subsequent end-of-cell assignments will define axioms" print "%end_axioms will finalize the definitions" def end_axioms(self): self._finish('axioms') def begin_theorems(self): # context based upon current working directory if len(self.definitions) > 0 or self.kind is not None: if self.kind != 'theorems': raise ProveItMagicFailure( "Run %%begin_theorems in a separate notebook from %%begin_%s." % self.kind) print "WARNING: Re-running %begin_theorems does not reset previously defined theorems." print " It is suggested that you restart and run all cells after editing theorems." print "Defining theorems for context '" + self.context.name + "'" print "Subsequent end-of-cell assignments will define theorems" print "'%end theorems' will finalize the definitions" def end_theorems(self): self._finish('theorems') # stash proof notebooks that are not active theorems. self.context.stashExtraneousProofNotebooks() def begin_common(self): if len(self.definitions) > 0 or self.kind is not None: if self.kind != 'common': raise ProveItMagicFailure( "Run '%%begin common' in a separate notebook from %%begin_%s." % self.kind) print "WARNING: Re-running '%begin common' does not reset previously defined common expressions." print " It is suggested that you restart and run all cells after editing the expressions." print "Defining common sub-expressions for context '" + self.context.name + "'" print "Subsequent end-of-cell assignments will define common sub-expressions" print "%end_common will finalize the definitions" def end_common(self): # Record the context names of common expressions referenced # by this context's common expressions notebook... self.context.recordCommonExprDependencies() # and check for illegal mutual references. cyclically_referenced_common_expr_context = self.context.cyclicallyReferencedCommonExprContext( ) if cyclically_referenced_common_expr_context is not None: raise ProveItMagicFailure( "Not allowed to have cyclically dependent 'common expression' notebooks: %s._common_" % cyclically_referenced_common_expr_context) self._finish('common') @line_magic def begin(self, line): kind = line.strip() # context based upon current working directory self.context = Context() if kind == 'axioms': self.begin_axioms() elif kind == 'theorems': self.begin_theorems() elif kind == 'common': self.begin_common() self.kind = kind @line_magic def end(self, line): kind = line.strip() if kind == 'axioms': self.end_axioms() elif kind == 'theorems': self.end_theorems() elif kind == 'common': self.end_common() # reference any expressions that were displayed: self.context.referenceDisplayedExpressions(kind) # clean unreferenced expressions: self.context.clean() self.kind = None @line_magic def clear(self, line): kind = line.strip() # context based upon current working directory self.context = Context() if kind == 'axioms': self.context._clearAxioms() elif kind == 'theorems': self.context._clearTheorems() elif kind == 'common': self.context._clearCommonExressions() elif KnownTruth.theoremBeingProven is not None: kind = '_proof_' + KnownTruth.theoremBeingProven.name # clean unreferenced expressions: self.context.referenceDisplayedExpressions(kind, clear=True) self.context.clean() self.kind = None @line_magic def check_expr(self, line): _, hash_id = os.path.split(os.path.abspath('.')) context = Context() expr_name = line.strip() if expr_name == '': expr_name = 'expr' expr = self.shell.user_ns[expr_name] else: expr = self.shell.user_ns[expr_name] if isinstance(expr, KnownTruth): # actually a KnownTruth; convert to an Expression expr = expr.expr stored_expr = context.getStoredExpr(hash_id) if expr != stored_expr: raise ProveItMagicFailure( "The built '%s' does not match the stored Expression" % expr_name) if expr._style_id != stored_expr._style_id: raise ProveItMagicFailure( "The built '%s' style does not match that of the stored Expression" % expr_name) print "Passed sanity check: built '%s' is the same as the stored Expression." % expr_name @line_magic def proving(self, line): from proveit._core_.proof import Theorem self.context = Context( '..' ) # the context should be up a directory from the _proofs_ directory sys.path.append('..') theorem_name, presuming_str = str(line.strip()).split(' ', 1) if not presuming_str.find('presuming ') == 0: print "Format: %begin_proof <theorem_name> presuming [<list of theorems / context-names>]" return args = presuming_str.split(' ', 1)[-1].strip('[]').split(',') theorem_truth = Context('..').getTheorem(theorem_name).provenTruth print "Beginning proof of", theorem_name presuming = [arg.strip() for arg in args if arg.strip() != ''] # The list of theorems/context-names may be composed of full-path strings containing '.'s # or may be actual theorem variables defined in the IPython sesson. The latter # instances will be converted to strings. for k, arg in enumerate(list(presuming)): if '.' not in arg: knownTruth = self.shell.user_ns[arg] if not isinstance(knownTruth, KnownTruth) or not isinstance( knownTruth.proof(), Theorem): raise ValueError( "Presuming list must be composed of full-path theorem/context-name containing '.'s or be KnownTruth variable representing a Theorem" ) theorem = knownTruth.proof() presuming[k] = str(theorem) # full path of theorem begin_proof_result = theorem_truth.beginProof(presuming) if isinstance(begin_proof_result, Expression): # assign the theorem name to the theorem expression # and display this assignment theorem_expr = theorem_truth.expr self.shell.user_ns[theorem_name] = theorem_expr return Assignments([theorem_name], [theorem_expr], beginningProof=True) @line_magic def qed(self, line): proof = KnownTruth.theoremBeingProven.provenTruth._qed() proof._repr_html_() # generate expressions that should be referenced self.context.referenceDisplayedExpressions( '_proof_' + KnownTruth.theoremBeingProven.name) # clean unreferenced expressions: self.context.clean() return proof def _finish(self, kind): ''' Finish 'axioms', 'theorems', or 'common' for the Context associated with the current working directory. ''' if self.kind != kind: raise ProveItMagicFailure(r"Must run %begin " + kind + r" before %end " + kind) # Add the special statements / expressions to the context context = self.context if kind == 'axioms': context._setAxioms(self.keys, self.definitions) elif kind == 'theorems': context._setTheorems(self.keys, self.definitions) elif kind == 'common': context._setCommonExpressions(self.keys, self.definitions) # Make a _common_.py, _axioms_.py or _theorems_.py for importing # expressions from the certified database. context.makeSpecialExprModule(kind) # Update the expression notebooks now that these have been registered # as special expressions. for name, expr in self.definitions.iteritems(): # remake the expression notebooks using the special expressions of the context context.expressionNotebook(expr) if len(self.definitions) == 0: print "Context %s has no %s" % (context.name, kind if kind != 'common' else 'common expressions') elif kind == 'common': print "Common expressions may be imported from autogenerated _%s_.py" % kind else: print "%s may be imported from autogenerated _%s_.py" % ( (kind[0].upper() + kind[1:]), kind) self.ranFinish = True @line_magic def dependencies(self, line): ''' Show the dependencies of an axiom or theorem. ''' from .proof import Theorem name = line.strip() known_truth = self.shell.user_ns[line.strip()] proof = known_truth.proof() # Axiom or Theorem def displaySpecialStmt(stmt): ''' Given an Axiom or Theorem, display HTML with a link to the definition. ''' expr = stmt.provenTruth.expr display( HTML( '<dt><a class="ProveItLink" href="%s">%s</a></dt><dd>%s</dd>' % (stmt.getLink(), str(stmt), expr._repr_html_()))) def stmt_sort(stmt): return str(stmt) if isinstance(proof, Theorem): try: required_axioms, required_unproven_theorems = proof.allRequirements( ) except: display(HTML('<h3>This theorem has not been proven yet.</h3>')) required_axioms, required_unproven_theorems = tuple(), tuple() if len(required_unproven_theorems) > 0: display( HTML( '<h3>Unproven theorems required (directly or indirectly) to prove %s</h3>' % name)) display(HTML('<dl>')) for required_unproven_theorem in sorted( required_unproven_theorems, key=stmt_sort): displaySpecialStmt( Context.findTheorem(required_unproven_theorem)) display(HTML('</dl>')) if len(required_axioms) > 0: display( HTML( '<h3>Axioms required (directly or indirectly) to prove %s</h3>' % name)) display(HTML('<dl>')) for required_axiom in sorted(required_axioms, key=stmt_sort): displaySpecialStmt(Context.findAxiom(required_axiom)) display(HTML('</dl>')) dependents = proof.directDependents() if len(dependents) == 0: display(HTML('<h3>No theorems depend upon %s</h3>' % name)) else: display(HTML('<h3>Theorems that depend directly on %s</h3>' % name)) display(HTML('<dl>')) for dependent in sorted(proof.directDependents(), key=stmt_sort): displaySpecialStmt(Context.findTheorem(dependent)) display(HTML('</dl>'))
class ProveItMagicCommands: def __init__(self): self.reset() def reset(self): # You must call the parent constructor self.kind = None self.definitions = dict() # map name to expression self.expr_names = dict() # map expression to names self.keys = [] # the keys of the definitions in the order they appear self.lowerCaseNames = set() self.context = None self.ranFinish = False def display_contents(self, context_names): ''' Generates a "table of contents" hierarchy of contexts for the contexts listed in the line. ''' def generateContents(contexts): if len(contexts) == 0: return '' html = '<ul>\n' for context in contexts: href = relurl( os.path.join(context.getPath(), '_context_.ipynb')) html += '<li><a class="ProveItLink" href="%s">%s</a></li>\n' % ( href, context.name) html += generateContents(list(context.getSubContexts())) return html + '</ul>\n' display( HTML( generateContents([ Context(context_name) for context_name in context_names ]))) def display_context(self): ''' Create the _common_, _axioms_ and _theorems_ notebooks for the current context (if they do not already exist). Show the table of contents for sub-contexts which may be edited. ''' import proveit # create an '__init__.py' in the directory if there is not an existing one. if not os.path.isfile('__init__.py'): open('__init__.py', 'w').close() # create an empty __init__.py context = Context() proveit_path = os.path.split(proveit.__file__)[0] special_notebook_types = ('common', 'axioms', 'theorems', 'demonstrations') special_notebook_texts = ('common expressions', 'axioms', 'theorems', 'demonstrations') for special_notebook_type in special_notebook_types: notebook_name = '_%s_.ipynb' % special_notebook_type if not os.path.isfile(notebook_name): # notebook does not yet exist, create it from the template template_name = '_%s_template_.ipynb' % special_notebook_type with open(os.path.join(proveit_path, '..', template_name), 'r') as template: nb = template.read() nb = nb.replace('#CONTEXT#', context.name) # write the notebook file with open(notebook_name, 'w') as notebook_file: notebook_file.write(nb) context_interface = ContextInterface() if context_interface.mode == 'static': special_notebooks_html = '<table><tr>\n' for special_notebook_type, special_notebook_text in zip( special_notebook_types, special_notebook_texts): special_notebooks_html += '<th><a class="ProveItLink" href="_%s_.ipynb">%s</a></th>\n' % ( special_notebook_type, special_notebook_text) special_notebooks_html += '</tr></table>\n' if len(context_interface.subContextNames) > 0: special_notebooks_html += '<table>\n' for name in context_interface.subContextNames: description = context_interface.subContextDescriptions[ name] href = context_interface.subContextNotebook(name) special_notebooks_html += '<tr><th><a class="ProveItLink" href="%s">%s</a></th><td>%s</td></tr>\n' % ( href, name, description) special_notebooks_html += '</table>\n' display(HTML(special_notebooks_html)) else: special_notebook_links = [] full_width_layout = widgets.Layout(width='100%', padding='5px') for special_notebook_type, special_notebook_text in zip( special_notebook_types, special_notebook_texts): special_notebook_links.append( widgets.HTML( '<a class="ProveItLink" href="_%s_.ipynb">%s</a>' % (special_notebook_type, special_notebook_text), layout=full_width_layout)) special_notebook_links = widgets.HBox(special_notebook_links) sub_contexts_label = widgets.Label( 'List of sub-contexts:', layout=widgets.Layout(width='100%')) #sub_context_widgets = widgets.VBox(sub_context_widgets) add_context_widget = widgets.Text(value='', placeholder='Add sub-context...') def addSubContext(sender): context_interface.addSubContext(add_context_widget.value) add_context_widget.value = '' add_context_widget.on_submit(addSubContext) #layout = widgets.Layout(display='flex', flex_flow='column-reverse') #display(widgets.Button(description='Edit...', disabled=False, button_style='', tooltip='Edit the sub-contents list', layout=layout)) #layout = widgets.Layout(float='bottom') display( widgets.VBox([ special_notebook_links, sub_contexts_label, context_interface.widget, add_context_widget ])) def begin_axioms(self): # context based upon current working directory self.context = Context() if len(self.definitions) > 0 or self.kind is not None: if self.kind != 'axioms': raise ProveItMagicFailure( "Run %%begin_axioms in a separate notebook from %%begin_%s." % self.kind) print( "WARNING: Re-running %begin_axioms does not reset previously defined axioms." ) print( " It is suggested that you restart and run all cells after editing axioms." ) print("Defining axioms for context '" + self.context.name + "'") print("Subsequent end-of-cell assignments will define axioms") print("%end_axioms will finalize the definitions") def begin_theorems(self): # context based upon current working directory if len(self.definitions) > 0 or self.kind is not None: if self.kind != 'theorems': raise ProveItMagicFailure( "Run %%begin_theorems in a separate notebook from %%begin_%s." % self.kind) print( "WARNING: Re-running %begin_theorems does not reset previously defined theorems." ) print( " It is suggested that you restart and run all cells after editing theorems." ) print("Defining theorems for context '" + self.context.name + "'") print("Subsequent end-of-cell assignments will define theorems") print("'%end theorems' will finalize the definitions") def begin_common(self): if len(self.definitions) > 0 or self.kind is not None: if self.kind != 'common': raise ProveItMagicFailure( "Run '%%begin common' in a separate notebook from %%begin_%s." % self.kind) print( "WARNING: Re-running '%begin common' does not reset previously defined common expressions." ) print( " It is suggested that you restart and run all cells after editing the expressions." ) print("Defining common sub-expressions for context '" + self.context.name + "'") print( "Subsequent end-of-cell assignments will define common sub-expressions" ) print("%end_common will finalize the definitions") def clear(self, kind): # context based upon current working directory self.context = Context() if kind == 'axioms': self.context._clearAxioms() elif kind == 'theorems': self.context._clearTheorems() elif kind == 'common': self.context._clearCommonExressions() elif KnownTruth.theoremBeingProven is not None: kind = '_proof_' + KnownTruth.theoremBeingProven.name # clean unreferenced expressions: self.context.referenceDisplayedExpressions(kind, clear=True) self.context.clean() self.kind = None def check_expr(self, expr_name, expr): _, hash_id = os.path.split(os.path.abspath('.')) context = Context() stored_expr = context.getStoredExpr(hash_id) if expr != stored_expr: raise ProveItMagicFailure( "The built '%s' does not match the stored Expression" % expr_name) if expr._style_id != stored_expr._style_id: raise ProveItMagicFailure( "The built '%s' style does not match that of the stored Expression" % expr_name) print( "Passed sanity check: built '%s' is the same as the stored Expression." % expr_name) def proving(self, theorem_name, presumptions, justRecordPresumingInfo=False): self.context = Context( '..' ) # the context should be up a directory from the _proofs_ directory sys.path.append('..') proving_theorem = self.context.getTheorem(theorem_name) proving_theorem_truth = proving_theorem.provenTruth print("Beginning proof of", theorem_name) return proving_theorem_truth.beginProof( proving_theorem, presumptions, justRecordPresumingInfo=justRecordPresumingInfo) def qed(self): proof = KnownTruth.theoremBeingProven.provenTruth._qed() proof._repr_html_() # generate expressions that should be referenced self.context.referenceDisplayedExpressions( '_proof_' + KnownTruth.theoremBeingProven.name) # clean unreferenced expressions: self.context.clean() return proof def end(self, kind): ''' Finish 'axioms', 'theorems', 'common', or other (e.g., 'demonstrations') for the Context associated with the current working directory. ''' if self.kind != kind: raise ProveItMagicFailure(r"Must run %begin " + kind + r" before %end " + kind) # Add the special statements / expressions to the context context = self.context if kind == 'axioms': context._setAxioms(self.keys, self.definitions) elif kind == 'theorems': context._setTheorems(self.keys, self.definitions) elif kind == 'common': # Record the context names of common expressions referenced # by this context's common expressions notebook... context.recordCommonExprDependencies() # and check for illegal mutual references. cyclically_referenced_common_expr_context = self.context.cyclicallyReferencedCommonExprContext( ) if cyclically_referenced_common_expr_context is not None: raise ProveItMagicFailure( "Not allowed to have cyclically dependent 'common expression' notebooks: %s._common_" % cyclically_referenced_common_expr_context) context._setCommonExpressions(self.keys, self.definitions) if kind in ('axioms', 'theorems', 'common'): # Make a _common_.py, _axioms_.py or _theorems_.py for importing # expressions from the certified database. context.makeSpecialExprModule(kind) # Update the expression notebooks now that these have been registered # as special expressions. for name, expr in self.definitions.items(): # remake the expression notebooks using the special expressions of the context context.expressionNotebook(expr) if len(self.definitions) == 0: print("Context %s has no %s" % (context.name, kind if kind != 'common' else 'common expressions')) elif kind == 'common': print( "Common expressions may be imported from autogenerated _%s_.py" % kind) else: print("%s may be imported from autogenerated _%s_.py" % ((kind[0].upper() + kind[1:]), kind)) self.ranFinish = True # reference any expressions that were displayed: self.context.referenceDisplayedExpressions(kind) # clean unreferenced expressions: self.context.clean() if kind == 'theorems': # stash proof notebooks that are not active theorems. self.context.stashExtraneousProofNotebooks() self.kind = None def display_dependencies(self, name, known_truth): ''' Show the dependencies of an axiom or theorem. ''' proof = known_truth.proof() # Axiom or Theorem def displaySpecialStmt(stmt): ''' Given an Axiom or Theorem, display HTML with a link to the definition. ''' expr = stmt.provenTruth.expr display( HTML( '<dt><a class="ProveItLink" href="%s">%s</a></dt><dd>%s</dd>' % (stmt.getLink(), str(stmt), expr._repr_html_()))) def stmt_sort(stmt): return str(stmt) if isinstance(proof, Theorem): try: required_axioms, required_unproven_theorems = proof.allRequirements( ) except: display(HTML('<h3>This theorem has not been proven yet.</h3>')) required_axioms, required_unproven_theorems = tuple(), tuple() if len(required_unproven_theorems) > 0: display( HTML( '<h3>Unproven theorems required (directly or indirectly) to prove %s</h3>' % name)) display(HTML('<dl>')) for required_unproven_theorem in sorted( required_unproven_theorems, key=stmt_sort): displaySpecialStmt( Context.findTheorem(required_unproven_theorem)) display(HTML('</dl>')) if len(required_axioms) > 0: display( HTML( '<h3>Axioms required (directly or indirectly) to prove %s</h3>' % name)) display(HTML('<dl>')) for required_axiom in sorted(required_axioms, key=stmt_sort): displaySpecialStmt(Context.findAxiom(required_axiom)) display(HTML('</dl>')) dependents = proof.directDependents() if len(dependents) == 0: display(HTML('<h3>No theorems depend upon %s</h3>' % name)) else: display(HTML('<h3>Theorems that depend directly on %s</h3>' % name)) display(HTML('<dl>')) for dependent in sorted(proof.directDependents(), key=stmt_sort): displaySpecialStmt(Context.findTheorem(dependent)) display(HTML('</dl>'))