Skip to content

REPL

bayescalc.repl

This module implements the interactive REPL for the Bayesian Network calculator.

Classes

REPL

Source code in src/bayescalc/repl.py
class REPL:
    def __init__(self, network: BayesianNetwork):
        self.network = network
        self.query_parser = QueryParser(network)
        self.command_handler = CommandHandler(
            network, reload_callback=self._reload_network
        )
        self.expression_parser = ExpressionParser(self.query_parser)

        # Type annotation to allow both PromptToolkitCompleter and None
        self.completer: Optional[object] = None
        if PROMPT_TOOLKIT_AVAILABLE:
            self.completer = PromptToolkitCompleter(network)
        else:
            self.completer = None
        self.history_file = ".bayescalc_history"

        # Type annotation to allow both PromptSession and None
        self.session: Optional[Any] = None
        if PROMPT_TOOLKIT_AVAILABLE:
            self.session = PromptSession(
                history=FileHistory(self.history_file),
                auto_suggest=AutoSuggestFromHistory(),
                completer=self.completer,
                complete_while_typing=True,  # Enable tab completion while typing
            )
        else:
            self.session = None

    def _reload_network(self, new_network: BayesianNetwork):
        """Callback to update all internal references when network is reloaded."""
        self.network = new_network
        self.query_parser = QueryParser(new_network)
        self.expression_parser = ExpressionParser(self.query_parser)

        # Update completer with new network
        if PROMPT_TOOLKIT_AVAILABLE and self.completer:
            self.completer = PromptToolkitCompleter(new_network)
            # Update the session's completer
            if self.session:
                self.session.completer = self.completer

    def run(self):
        """Starts the REPL loop."""
        if not PROMPT_TOOLKIT_AVAILABLE:
            raise RuntimeError("REPL requires prompt_toolkit to be available")

        print("Bayesian Network Calculator")
        print("Type 'help' for a list of commands, 'exit' to quit.")

        while True:
            try:
                line = self.session.prompt(">> ").strip()
                if not line:
                    continue

                if line.lower() == "exit":
                    break
                elif line.lower() == "help":
                    self.print_help()
                    continue

                # Check if it's a known command first (before trying expression evaluation)
                # This prevents commands like load(...) from being treated as mathematical expressions
                if self.command_handler.is_command(line):
                    try:
                        result = self.command_handler.execute(line)
                        print(result)
                    except (ValueError, SyntaxError, KeyError) as e:
                        print(f"Error: {e}", file=sys.stderr)
                    continue

                # Check if it can be evaluated as an expression (mathematical or probability)
                if self.expression_parser.can_evaluate(line):
                    try:
                        result = self.expression_parser.evaluate(line)
                        if hasattr(result, "probabilities"):  # It's a Factor object
                            # Print the result as a distribution
                            for assignment, prob in result.probabilities.items():
                                print(
                                    f"  P({', '.join(assignment) if assignment else ''}) = {prob:.6f}"
                                )
                        else:  # It's a scalar value
                            print(f"  = {result:.6f}")
                    except ValueError as e:
                        print(f"Error: {e}", file=sys.stderr)
                    continue

                # If we reach here, it's neither a command nor an expression
                # This should rarely happen, but provide a helpful error message
                print(
                    f"Error: Unknown command or invalid expression: {line}",
                    file=sys.stderr,
                )
                print("Type 'help' for a list of available commands.", file=sys.stderr)

            except (ValueError, SyntaxError, KeyError) as e:
                print(f"Error: {e}", file=sys.stderr)
            except (KeyboardInterrupt, EOFError):
                print("\nExiting.")
                break

    def print_help(self):
        """Prints the help message."""
        help_text = """
Available commands:
  P(A, B | C=c, D=d)   - Compute conditional probability.
  Arithmetic Expressions - Compute with probabilities:
    P(A=a)/P(B=b)      - Divide probabilities
    P(A=a)+P(A=b)      - Sum of probabilities
    P(A|B=b)*2-0.5     - Operations with constants
  Mathematical Functions - Pure math or with probabilities:
    log10(0.5)         - Base-10 logarithm
    sqrt(2)            - Square root
    exp(1)             - Exponential
    sin(0), cos(0)     - Trigonometric functions
    log10(P(A=a))      - Apply math functions to probabilities
    sqrt(P(A=a)) * 2   - Combine math and probability operations
  printCPT(X)          - Print the Conditional Probability Table for variable X.
  printJPT()           - Print the full Joint Probability Table.
  parents(X)           - Show the parents of variable X.
  children(X)          - Show the children of variable X.
  showGraph()          - Display an ASCII graph of the network.
  isindependent(A, B)  - Check if variables A and B are independent.
  iscondindependent(A, B | C) - Check for conditional independence.
  entropy(X)           - Compute the entropy of variable X.
  conditional_entropy(X|Y) - Compute conditional entropy H(X|Y).
  mutual_information(X, Y) - Compute mutual information between X and Y.
  visualize(file, format=pdf, show_cpt=True) - Generate network visualization.
  load(filename)       - Load a new Bayesian network from a file.
  help()               - Show detailed help for all commands.
  help(command)        - Show detailed help for a specific command.
  exit                 - Exit the calculator.
  ls / vars            - List all defined variables and their states.

Note: Use 'help()' with parentheses for detailed command documentation.
"""
        print(help_text)
Functions
run()

Starts the REPL loop.

Source code in src/bayescalc/repl.py
def run(self):
    """Starts the REPL loop."""
    if not PROMPT_TOOLKIT_AVAILABLE:
        raise RuntimeError("REPL requires prompt_toolkit to be available")

    print("Bayesian Network Calculator")
    print("Type 'help' for a list of commands, 'exit' to quit.")

    while True:
        try:
            line = self.session.prompt(">> ").strip()
            if not line:
                continue

            if line.lower() == "exit":
                break
            elif line.lower() == "help":
                self.print_help()
                continue

            # Check if it's a known command first (before trying expression evaluation)
            # This prevents commands like load(...) from being treated as mathematical expressions
            if self.command_handler.is_command(line):
                try:
                    result = self.command_handler.execute(line)
                    print(result)
                except (ValueError, SyntaxError, KeyError) as e:
                    print(f"Error: {e}", file=sys.stderr)
                continue

            # Check if it can be evaluated as an expression (mathematical or probability)
            if self.expression_parser.can_evaluate(line):
                try:
                    result = self.expression_parser.evaluate(line)
                    if hasattr(result, "probabilities"):  # It's a Factor object
                        # Print the result as a distribution
                        for assignment, prob in result.probabilities.items():
                            print(
                                f"  P({', '.join(assignment) if assignment else ''}) = {prob:.6f}"
                            )
                    else:  # It's a scalar value
                        print(f"  = {result:.6f}")
                except ValueError as e:
                    print(f"Error: {e}", file=sys.stderr)
                continue

            # If we reach here, it's neither a command nor an expression
            # This should rarely happen, but provide a helpful error message
            print(
                f"Error: Unknown command or invalid expression: {line}",
                file=sys.stderr,
            )
            print("Type 'help' for a list of available commands.", file=sys.stderr)

        except (ValueError, SyntaxError, KeyError) as e:
            print(f"Error: {e}", file=sys.stderr)
        except (KeyboardInterrupt, EOFError):
            print("\nExiting.")
            break
print_help()

Prints the help message.

Source code in src/bayescalc/repl.py
    def print_help(self):
        """Prints the help message."""
        help_text = """
Available commands:
  P(A, B | C=c, D=d)   - Compute conditional probability.
  Arithmetic Expressions - Compute with probabilities:
    P(A=a)/P(B=b)      - Divide probabilities
    P(A=a)+P(A=b)      - Sum of probabilities
    P(A|B=b)*2-0.5     - Operations with constants
  Mathematical Functions - Pure math or with probabilities:
    log10(0.5)         - Base-10 logarithm
    sqrt(2)            - Square root
    exp(1)             - Exponential
    sin(0), cos(0)     - Trigonometric functions
    log10(P(A=a))      - Apply math functions to probabilities
    sqrt(P(A=a)) * 2   - Combine math and probability operations
  printCPT(X)          - Print the Conditional Probability Table for variable X.
  printJPT()           - Print the full Joint Probability Table.
  parents(X)           - Show the parents of variable X.
  children(X)          - Show the children of variable X.
  showGraph()          - Display an ASCII graph of the network.
  isindependent(A, B)  - Check if variables A and B are independent.
  iscondindependent(A, B | C) - Check for conditional independence.
  entropy(X)           - Compute the entropy of variable X.
  conditional_entropy(X|Y) - Compute conditional entropy H(X|Y).
  mutual_information(X, Y) - Compute mutual information between X and Y.
  visualize(file, format=pdf, show_cpt=True) - Generate network visualization.
  load(filename)       - Load a new Bayesian network from a file.
  help()               - Show detailed help for all commands.
  help(command)        - Show detailed help for a specific command.
  exit                 - Exit the calculator.
  ls / vars            - List all defined variables and their states.

Note: Use 'help()' with parentheses for detailed command documentation.
"""
        print(help_text)