Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
231 commits
Select commit Hold shift + click to select a range
fedea15
chore: Added a comment about allow_transitive_resources to tutorial #1
Brendan-Reid1991 May 8, 2025
e9519f6
chore: added allow_transitive_resources=False; added quotes to Note a…
Brendan-Reid1991 May 8, 2025
a7f94dc
chore: updated tutorial 3
Brendan-Reid1991 May 8, 2025
89cbc8f
chore: Added sentences about transitive compilation of resources
Brendan-Reid1991 May 8, 2025
a0a0586
refactor: Created `CompilationFlags` enum
Brendan-Reid1991 May 8, 2025
540c638
refactor: Replaced asserts with Exceptions; added fix for repetitions…
Brendan-Reid1991 May 9, 2025
76a4f64
refactor: Added CompilationFlags to init
Brendan-Reid1991 May 9, 2025
06e0a8d
chore: Updated docstrings
Brendan-Reid1991 May 9, 2025
6d2fda2
chore: updating tests for CompilationFlags
Brendan-Reid1991 May 9, 2025
93b841b
chore: Modified tests to catch edge case errors (previously assertions)
Brendan-Reid1991 May 9, 2025
b7271b7
refactor: Changed assertions into Exceptions
Brendan-Reid1991 May 9, 2025
c659b55
bugfix: Changed None -> CompilationsFlags(0)
Brendan-Reid1991 May 9, 2025
59207e6
chore: Updated compilation to discuss transitivity and compilation flags
Brendan-Reid1991 May 9, 2025
473ffd8
chore: Updated tutorials with Compilation Flags
Brendan-Reid1991 May 9, 2025
b7753c1
refactor: creating analysis submodule.
Brendan-Reid1991 May 12, 2025
8ee76d7
feat: Began work on sympy manipulator
Brendan-Reid1991 May 12, 2025
9c4665f
feat: added instruction types enum; adding functionality to SM
Brendan-Reid1991 May 14, 2025
84c8dfa
refactor: split base class + sympyManipulator into separate modules
Brendan-Reid1991 May 14, 2025
b6e14c7
chore: removed generic types
Brendan-Reid1991 May 14, 2025
415d2cf
refactor: Moved InstructionsType enum
Brendan-Reid1991 May 14, 2025
af32cbf
feat: Added `focus` method. Generic types added
Brendan-Reid1991 May 14, 2025
2bf8f30
refactor: Renamed SympyManipulation -> SympyManipulator
Brendan-Reid1991 May 14, 2025
efea576
fix: removed testing code
Brendan-Reid1991 May 14, 2025
b5dcdf4
refactor: renamed analysis.py -> optimization.py
Brendan-Reid1991 May 15, 2025
3f8f32a
refactor: created analysis module
Brendan-Reid1991 May 15, 2025
b98cf54
feat: created rewriters module
Brendan-Reid1991 May 15, 2025
03427b5
feat: added expressionwriter abc
Brendan-Reid1991 May 15, 2025
e0dd510
feat: subclassed from expressionwriter
Brendan-Reid1991 May 15, 2025
350d422
chore: improving typing
Brendan-Reid1991 May 21, 2025
c62ec31
feat: Added basic tests for rewriters
Brendan-Reid1991 May 27, 2025
176b25d
chore: improved typing, added docstrings
Brendan-Reid1991 May 27, 2025
122a23c
feat: improved typing; added docstrings
Brendan-Reid1991 May 27, 2025
ce196c2
chore: ran isort
Brendan-Reid1991 May 27, 2025
2b525c0
Merge branch 'main' into symbolic_manipulator
Brendan-Reid1991 May 27, 2025
e071b86
chore: fixed typing issues
Brendan-Reid1991 May 27, 2025
e4fba55
chore: added module level docstrings
Brendan-Reid1991 May 27, 2025
2bb75cd
chore: removed unused import
Brendan-Reid1991 May 27, 2025
41ab585
chore: erroneous line introduced
Brendan-Reid1991 May 27, 2025
1b270ab
chore: added module level docstring
Brendan-Reid1991 May 27, 2025
6b3f738
chore: improved typing in update_expression
Brendan-Reid1991 May 28, 2025
26de84c
chore: added module level docstring; removed return section of docstr…
Brendan-Reid1991 May 28, 2025
29bc2ca
chore: Type -> type
Brendan-Reid1991 May 28, 2025
e2e43f3
Merge branch 'main' into symbolic_manipulator
Brendan-Reid1991 May 28, 2025
25d4f8e
chore: made expression_rewriter.py public
Brendan-Reid1991 May 29, 2025
08b64f2
refactor: renamed variables -> free_symbols
Brendan-Reid1991 May 29, 2025
b483165
refactor: improved docstrings
Brendan-Reid1991 May 29, 2025
ff2282a
chore: correcting imports
Brendan-Reid1991 May 29, 2025
fa3e350
refactor: renamed test_variables, and improved it such that it will w…
Brendan-Reid1991 May 29, 2025
bd41a40
Merge branch 'main' into symbolic_manipulator
Brendan-Reid1991 May 29, 2025
2c51d22
Merge branch 'main' into symbolic_manipulator
Brendan-Reid1991 May 30, 2025
6dfddb8
chore: added copyright message
Brendan-Reid1991 May 30, 2025
1a656b5
refactor: renamed free_symbols_in -> free_symbols
Brendan-Reid1991 Jun 2, 2025
5310bea
chore: updated docstrings
Brendan-Reid1991 Jun 2, 2025
d252149
chore: isort fix
Brendan-Reid1991 Jun 2, 2025
2fddca3
docs: improve the API docs (#209)
AVDiv Jun 1, 2025
9f0c5a5
feat: began development on assumptions
Brendan-Reid1991 Jun 2, 2025
58eb2b7
chore: Added a comment about allow_transitive_resources to tutorial #1
Brendan-Reid1991 May 8, 2025
442e6ab
chore: added allow_transitive_resources=False; added quotes to Note a…
Brendan-Reid1991 May 8, 2025
cb1f7b5
chore: updated tutorial 3
Brendan-Reid1991 May 8, 2025
d64f9aa
chore: Added sentences about transitive compilation of resources
Brendan-Reid1991 May 8, 2025
30c8c25
refactor: Created `CompilationFlags` enum
Brendan-Reid1991 May 8, 2025
9fff558
refactor: Replaced asserts with Exceptions; added fix for repetitions…
Brendan-Reid1991 May 9, 2025
82df742
chore: Updated docstrings
Brendan-Reid1991 May 9, 2025
7b49072
chore: updating tests for CompilationFlags
Brendan-Reid1991 May 9, 2025
59a8718
chore: Modified tests to catch edge case errors (previously assertions)
Brendan-Reid1991 May 9, 2025
144e1a5
chore: Updated tutorials with Compilation Flags
Brendan-Reid1991 May 9, 2025
62e3eb0
refactor: creating analysis submodule.
Brendan-Reid1991 May 12, 2025
c77953d
docs: improve the API docs (#209)
AVDiv Jun 1, 2025
8263d94
Merge branch 'main' into symbolic_manipulator
Brendan-Reid1991 Jun 3, 2025
4cedabc
chore: resolving conflicts
Brendan-Reid1991 Jun 3, 2025
3c24a45
chore: free_symbols_in -> free_symbols
Brendan-Reid1991 Jun 3, 2025
5fb2e57
chore: free_symbols_in -> free_symbols
Brendan-Reid1991 Jun 3, 2025
48b22c3
Resolving conflicts
Brendan-Reid1991 Jun 3, 2025
efac47f
Merge branch 'main' into assumptions
Brendan-Reid1991 Jun 5, 2025
470701d
Improving assumptions handling
Brendan-Reid1991 Jun 9, 2025
b9ec5fc
Merge branch 'main' into assumptions
Brendan-Reid1991 Jun 9, 2025
691574c
began typing fixes
Brendan-Reid1991 Jun 11, 2025
3c7650c
Removed expression getter/setter. Updated typing
Brendan-Reid1991 Jun 11, 2025
e4e1d6c
Removed commented out code
Brendan-Reid1991 Jun 11, 2025
fe2dd73
feat: added treemap visualisation class (#216)
antalszava Jun 9, 2025
59289f5
chore: rename nlz to ntz (#225)
pqvr Jun 9, 2025
55b1278
Removed reference to ._expr
Brendan-Reid1991 Jun 11, 2025
a2a040f
improved typing
Brendan-Reid1991 Jun 11, 2025
e4d44cb
Modifying Basic -> Expr as generic type.
Brendan-Reid1991 Jun 11, 2025
81c553e
Modified fixture
Brendan-Reid1991 Jun 11, 2025
9e8a6d6
improved typing
Brendan-Reid1991 Jun 11, 2025
2285c16
Moved expand test to be sympy specific
Brendan-Reid1991 Jun 11, 2025
0335814
changed fixture scope from 'class' to 'function'
Brendan-Reid1991 Jun 11, 2025
8d605a5
renamed as_individual_terms - > individual_terms
Brendan-Reid1991 Jun 11, 2025
8ee9350
Renamed property; ran isort
Brendan-Reid1991 Jun 11, 2025
914159b
renamed as_individual_terms -> individual_terms
Brendan-Reid1991 Jun 11, 2025
909098f
typo fix
Brendan-Reid1991 Jun 11, 2025
f1ebb16
Removed fixtures from tests and restructured them
Brendan-Reid1991 Jun 11, 2025
30e5bd0
chore: update poetry.lock file for dependabot (#227)
pqvr Jun 11, 2025
6520758
refactor: made pandas and plotly optional installs (#226)
Brendan-Reid1991 Jun 12, 2025
76f782d
updated substitute function to account for symbols with assumptions o…
Brendan-Reid1991 Jun 12, 2025
db15bc0
attempting to consolidate the typing
Brendan-Reid1991 Jun 12, 2025
d04e708
Merge branch 'main' into symbolic_manipulator
Brendan-Reid1991 Jun 12, 2025
b0dac31
Added sequence of commands as a test
Brendan-Reid1991 Jun 12, 2025
b684323
removing unused import
Brendan-Reid1991 Jun 12, 2025
b5f3b7d
Merge branch 'symbolic_manipulator' into assumptions
Brendan-Reid1991 Jun 12, 2025
729a982
Updating typing
Brendan-Reid1991 Jun 13, 2025
72278d4
Implementing Konrads changes
Brendan-Reid1991 Jun 13, 2025
ddf7beb
remove trailing whitespace
Brendan-Reid1991 Jun 13, 2025
df3c3ec
added return statement
Brendan-Reid1991 Jun 13, 2025
53ad6cf
reimplementing lost changes
Brendan-Reid1991 Jun 13, 2025
155406a
Merge branch 'symbolic_manipulator' into assumptions
Brendan-Reid1991 Jun 13, 2025
5b3ac52
Implementing SympyAssumption
Brendan-Reid1991 Jun 13, 2025
dbfaed9
made it so child classes of ExpressionRewriters do not need the decor…
Brendan-Reid1991 Jun 13, 2025
5682efa
moved evaluation check into Assumptions _post_init_
Brendan-Reid1991 Jun 16, 2025
962ce34
started assumptions tracking
Brendan-Reid1991 Jun 16, 2025
e4107a6
quick fix for custom max problem
Brendan-Reid1991 Jun 16, 2025
8d2dae3
Merge branch 'main' into symbolic_manipulator
Brendan-Reid1991 Jun 16, 2025
c56f540
improved docstrings and added comments
Brendan-Reid1991 Jun 16, 2025
9db41fb
added reapply_all_assumptions
Brendan-Reid1991 Jun 16, 2025
3291d60
formatting
Brendan-Reid1991 Jun 16, 2025
6cb0cf3
improving properties dict
Brendan-Reid1991 Jun 17, 2025
00eca05
isort
Brendan-Reid1991 Jun 17, 2025
3b1b0c3
formatting
Brendan-Reid1991 Jun 17, 2025
a9aa3db
began tests
Brendan-Reid1991 Jun 17, 2025
340d32d
updated properties dict
Brendan-Reid1991 Jun 17, 2025
e3398da
added tests
Brendan-Reid1991 Jun 17, 2025
cd8aa32
formatting
Brendan-Reid1991 Jun 17, 2025
de923a0
added comment on _add_assumption
Brendan-Reid1991 Jun 17, 2025
0b97e37
deleted test file
Brendan-Reid1991 Jun 17, 2025
7f03cf5
fixing mypy issues
Brendan-Reid1991 Jun 17, 2025
e630f00
fixing mypy issues
Brendan-Reid1991 Jun 17, 2025
d52be33
fixing import for py3.10
Brendan-Reid1991 Jun 17, 2025
5fd75ca
isort
Brendan-Reid1991 Jun 17, 2025
347d853
Merge branch 'main' into symbolic_manipulator
Brendan-Reid1991 Jun 18, 2025
bbb7d81
Merge branch 'symbolic_manipulator' into assumptions
Brendan-Reid1991 Jun 18, 2025
4033f07
minor typo fix
Brendan-Reid1991 Jun 18, 2025
ca10e9a
Added substitution methods
Brendan-Reid1991 Jun 19, 2025
0a081ed
implemented sympy substitutions,including wildcards
Brendan-Reid1991 Jun 19, 2025
c0769e9
modified substitute to allow arbitrary substitutions"
Brendan-Reid1991 Jun 19, 2025
fc35a33
Merge branch 'main' into substitutions
Brendan-Reid1991 Jun 20, 2025
147989a
improvements to wildcard subs
Brendan-Reid1991 Jun 20, 2025
8d75e57
Merge branch 'main' into symbolic_manipulator
Brendan-Reid1991 Jun 20, 2025
5c3a4a3
erge branch 'symbolic_manipulator' into assumptions
Brendan-Reid1991 Jun 20, 2025
ce68e6e
Merge branch 'assumptions' into substitutions
Brendan-Reid1991 Jun 20, 2025
884a45b
fixed final bug in wildcard subs
Brendan-Reid1991 Jun 20, 2025
a8ba8d9
added hash to assumptions
Brendan-Reid1991 Jun 20, 2025
b180073
Merge branch 'assumptions' into substitutions
Brendan-Reid1991 Jun 20, 2025
b912f6d
minor refactor
Brendan-Reid1991 Jun 23, 2025
ec7cfa3
added decorator to override custom max
Brendan-Reid1991 Jun 23, 2025
f17a79c
minor refactor after removing custom max
Brendan-Reid1991 Jun 23, 2025
4effe74
began improving tests
Brendan-Reid1991 Jun 23, 2025
1c6d628
merge main
Brendan-Reid1991 Jun 23, 2025
23b7cab
added classmethod to sympy_backend
Brendan-Reid1991 Jun 24, 2025
41dd129
added classmethod to sympy_backend
Brendan-Reid1991 Jun 24, 2025
f102207
added tests for .with_sympy_max
Brendan-Reid1991 Jun 24, 2025
bd01265
merging in classmethod
Brendan-Reid1991 Jun 24, 2025
78d6560
rename Relationals -> Comparators
Brendan-Reid1991 Jun 24, 2025
b9a6431
rename relationship->comparator
Brendan-Reid1991 Jun 24, 2025
cdc5efe
minor typo fix
Brendan-Reid1991 Jun 24, 2025
832310b
re-adding assumptions after mistaken deletion
Brendan-Reid1991 Jun 24, 2025
f54691c
changed from using float to literal_eval
Brendan-Reid1991 Jun 24, 2025
37e35cd
added more tests for assumptions
Brendan-Reid1991 Jun 24, 2025
61040f3
minor fixes
Brendan-Reid1991 Jun 24, 2025
278db6e
merge main
Brendan-Reid1991 Jun 24, 2025
fa622a7
merge assumptions
Brendan-Reid1991 Jun 24, 2025
17688f5
mypy fixes
Brendan-Reid1991 Jun 25, 2025
e17ce27
docstring updates;mypy fixes;wildcard improvements
Brendan-Reid1991 Jun 25, 2025
a7d2528
mypy fixes
Brendan-Reid1991 Jun 25, 2025
12a7cef
improved typehinting
Brendan-Reid1991 Jun 25, 2025
f1a8d36
improved tracking of assumptions
Brendan-Reid1991 Jun 25, 2025
09a05ca
improved tracking of assumptions
Brendan-Reid1991 Jun 25, 2025
20f9145
Merge branch 'main' into modify_sympy_backend
Brendan-Reid1991 Jun 25, 2025
b56c043
Merge branch 'modify_sympy_backend' into assumptions
Brendan-Reid1991 Jun 25, 2025
6d867d6
merging base
Brendan-Reid1991 Jun 25, 2025
44cbd16
updating evaluate from main
Brendan-Reid1991 Jun 25, 2025
56d199d
minor fixes
Brendan-Reid1991 Jun 25, 2025
eaebc77
gave _substitute default behaviour
Brendan-Reid1991 Jun 25, 2025
9a7ddd2
streamlined dataclass interactions
Brendan-Reid1991 Jun 25, 2025
2b2e060
deleting test file
Brendan-Reid1991 Jun 25, 2025
3624d9a
Merge branch 'modify_sympy_backend' into assumptions
Brendan-Reid1991 Jun 25, 2025
133cac4
added_named_tuple
Brendan-Reid1991 Jun 25, 2025
87be2a2
bug fixes
Brendan-Reid1991 Jun 25, 2025
399a087
change from all caps to snake case
Brendan-Reid1991 Jun 25, 2025
a4ed683
minor refactor
Brendan-Reid1991 Jun 25, 2025
33b5200
started testing substitutions
Brendan-Reid1991 Jun 25, 2025
f88fda5
moving some assumptions tests into basic tests
Brendan-Reid1991 Jun 25, 2025
ef5d8bd
merging assumptions
Brendan-Reid1991 Jun 25, 2025
82dd1da
updated focus + substitution logic
Brendan-Reid1991 Jun 26, 2025
c478c91
added tests
Brendan-Reid1991 Jun 26, 2025
692a92f
fixing docstring inconsistencies
Brendan-Reid1991 Jun 26, 2025
131e394
quick fixes from michals review
Brendan-Reid1991 Jun 26, 2025
65ca4ac
Removed classmethod; moved logic to `parse_to_sympy`
Brendan-Reid1991 Jun 26, 2025
ed0943c
modified interpreter to override certain functions
Brendan-Reid1991 Jun 26, 2025
4bd96c6
modified rewriter backend def
Brendan-Reid1991 Jun 26, 2025
6262d91
removed test until we settle on impl
Brendan-Reid1991 Jun 26, 2025
4c73640
bug fixes, test fixes
Brendan-Reid1991 Jun 26, 2025
d61c765
deleted unwanted file
Brendan-Reid1991 Jun 26, 2025
2d5d3ee
mypy fix
Brendan-Reid1991 Jun 26, 2025
84abb66
minor change
Brendan-Reid1991 Jun 26, 2025
a153198
refactor
Brendan-Reid1991 Jun 26, 2025
cb3e08e
added function_overrides to sympyinterpreter
Brendan-Reid1991 Jun 26, 2025
4c07c13
updated sympybackend with new properties
Brendan-Reid1991 Jun 26, 2025
8c378d8
updated rewriter after changes to sympy backend
Brendan-Reid1991 Jun 26, 2025
b34e00f
modified backend after changes to interpreter
Brendan-Reid1991 Jun 26, 2025
6abf5ca
removing unnecessary kwargs
Brendan-Reid1991 Jun 26, 2025
a94ad28
improved docstrings
Brendan-Reid1991 Jun 27, 2025
7c0daaf
added tests for sympy_backend when using sympy max
Brendan-Reid1991 Jun 27, 2025
39807b8
merge parent
Brendan-Reid1991 Jun 27, 2025
0360091
rename assume->assumption
Brendan-Reid1991 Jun 27, 2025
bb4e65b
fix typing error
Brendan-Reid1991 Jun 27, 2025
537816e
test renaming
Brendan-Reid1991 Jun 27, 2025
6ccb480
merging
Brendan-Reid1991 Jun 27, 2025
9eae4c8
refactored into dataclasses
Brendan-Reid1991 Jun 27, 2025
853389f
removing tests from downstream branch
Brendan-Reid1991 Jun 27, 2025
0c13560
merge main
Brendan-Reid1991 Jun 27, 2025
e977335
Merge branch 'assumptions' into refactor_rewriters
Brendan-Reid1991 Jun 27, 2025
0bb2788
added instruction set
Brendan-Reid1991 Jun 27, 2025
14cf860
renaming assumption->assume
Brendan-Reid1991 Jun 27, 2025
a929095
typo
Brendan-Reid1991 Jun 27, 2025
91eaffd
another typo
Brendan-Reid1991 Jun 27, 2025
2bd4142
renamed kwarg to assumption
Brendan-Reid1991 Jun 30, 2025
aa7c852
Merge assumptions
Brendan-Reid1991 Jun 30, 2025
43fe406
fixing typing & usability
Brendan-Reid1991 Jun 30, 2025
a9a66f1
updated tests to pass
Brendan-Reid1991 Jun 30, 2025
7e20e83
merge in main
Brendan-Reid1991 Jun 30, 2025
8b45883
fixing imports
Brendan-Reid1991 Jul 1, 2025
762edac
deleted erroneous line
Brendan-Reid1991 Jul 1, 2025
6181bd9
added tests for history tracking
Brendan-Reid1991 Jul 1, 2025
33b284e
deleted duplicate fn
Brendan-Reid1991 Jul 2, 2025
99b62b7
removed revert_to in favour of undo_previous; renamed show_history; i…
Brendan-Reid1991 Jul 2, 2025
b204bb6
fixed implementation error in reapply_all_assumptions
Brendan-Reid1991 Jul 2, 2025
b0b376f
fixed implementation error in focus
Brendan-Reid1991 Jul 2, 2025
4514c0b
added original; hardcoded sympy backend to prevent reinstantiation
Brendan-Reid1991 Jul 2, 2025
07c9f2d
modified history methods; updated tests
Brendan-Reid1991 Jul 2, 2025
621b9ef
reverting change to sympy_backend
Brendan-Reid1991 Jul 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 108 additions & 64 deletions src/bartiq/analysis/_rewriters/expression_rewriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,68 +17,110 @@

from abc import ABC, abstractmethod
from collections.abc import Callable, Iterable, Mapping
from numbers import Number
from typing import Any, Concatenate, Generic, ParamSpec, TypeVar, cast
from dataclasses import dataclass, field, replace
from enum import Enum
from typing import Generic, cast

from typing_extensions import Self

from bartiq import CompiledRoutine
from bartiq.analysis._rewriters.assumptions import Assumption
from bartiq.symbolics.backend import SymbolicBackend, T, TExpr

P = ParamSpec("P")

TRewriter = TypeVar("TRewriter", bound="ExpressionRewriter[Any]")
class Instruction(str, Enum):
"""A collection of transformative operations on rewriter instances."""

Initial = "initial"
"""The initial instance of a rewriter."""

def update_expression(
function: Callable[Concatenate[TRewriter, P], TExpr[T]],
) -> Callable[Concatenate[TRewriter, P], TExpr[T]]:
"""Decorator for updating the stored expression in ExpressionRewriter."""
Expand = "expand"
"""Expand all brackets in an expression."""

def _inner(self: TRewriter, *args: P.args, **kwargs: P.kwargs) -> TExpr[T]:
self.expression = function(self, *args, **kwargs)
return self.expression
Simplify = "simplify"
"""Simplify an expression."""

return _inner
ReapplyAllAssumptions = "reapply_all_assumptions"
"""Reapply all assumptions previously applied."""


@dataclass
class ExpressionRewriter(ABC, Generic[T]):
"""An abstract base class for rewriting expressions."""

def __init__(self, expression: T | str, backend: SymbolicBackend[T]):
self.expression = cast(TExpr[T], backend.as_expression(expression))
self.original_expression = self.expression
self._backend = backend
expression: TExpr[T] | str
backend: SymbolicBackend[T]
assumptions: tuple[Assumption, ...] = ()
linked_params: dict[T, Iterable[T]] = field(default_factory=dict)
_original_expression: TExpr[T] | str = ""
_previous: tuple[Instruction | str, Self | None] = (Instruction.Initial, None)

def __post_init__(self):
self.expression = cast(T, self.backend.as_expression(self.expression))
if self._original_expression == "":
self._original_expression = self.expression

self.applied_assumptions: tuple[Assumption, ...] = ()
def _repr_latex_(self) -> str | None:
if hasattr(self.expression, "_repr_latex_"):
return self.expression._repr_latex_()
return None

@property
def original(self) -> Self:
"""Return a rewriter with the original expression, and no modifications."""
return type(self)(expression=self._original_expression, backend=self.backend)

def _unwrap_history(self) -> list[tuple[Instruction | str, ExpressionRewriter[T] | None]]:
previous = []
current: ExpressionRewriter[T] | None = self
while current is not None:
previous.append(current._previous)
current = current._previous[1]
return previous

def history(self) -> list[Instruction | str]:
"""Show a chronological history of all rewriter-transforming commands that have resulted in this
instance of the rewriter.

Returns:
A list of chronologically ordered `Instructions`, where index 0 corresponds to initialisation.
"""
instructions, _ = zip(*self._unwrap_history())
return list(instructions[::-1])

def undo_previous(self, num_operations_to_undo: int = 1) -> Self:
"""Undo a number of previous operations.

Rewinds the rewriter back to a previous instance.

Args:
num_operations_to_undo: The number of previous steps to undo, by default 1.

Returns:
A previous instance of the rewriter.
"""
_, previous_instances = zip(*self._unwrap_history())
if num_operations_to_undo > (x := len(previous_instances) - 1):
raise ValueError(f"Attempting to undo too many operations! Only {x} transforming commands in history.")
if num_operations_to_undo < 1:
raise ValueError("Can't undo fewer than one previous command.")
return previous_instances[num_operations_to_undo - 1] or self.original

def evaluate_expression(
self,
assignments: Mapping[str, Number],
assignments: Mapping[str, TExpr[T]],
functions_map: Mapping[str, Callable[[TExpr[T]], TExpr[T]]] | None = None,
original_expression: bool = False,
) -> Number:
"""Assign explicit values to variables.
) -> TExpr[T]:
"""Temporarily evaluate the expression.

This function does not store the result! Will be refactored in a future PR for a cleaner interface.
This method does _not_ store the result, and employs the 'substitute' method of the given backend.

Args:
assignments : A dictionary of (variable: value) key, val pairs.
original_expression: Whether or not to evaluate the original expression, by default False.
functions_map: A map for certain functions.

Returns:
A fully or partially evaluated expression.
assignments: A mapping of symbols to numeric values or expressions.
functions_map: A mapping for user-defined functions in the expressions. By default None.
"""
if not all(isinstance(x, Number) for x in assignments.values()) or set(assignments.keys()) != self.free_symbols:
raise ValueError("You must pass in numeric values for all symbols in the expression. ")
return cast(
Number,
self._backend.substitute(
self.original_expression if original_expression else self.expression,
replacements=assignments, # type: ignore
# TODO: Remove this in future PR.
functions_map=functions_map,
),
return self.backend.substitute(
cast(TExpr[T], self.expression), replacements=assignments, functions_map=functions_map
)

@property
Expand All @@ -91,47 +133,49 @@ def free_symbols(self) -> Iterable[T]:
def individual_terms(self) -> Iterable[T]:
"""Return the expression as an iterable of individual terms."""

@abstractmethod
def focus(self, symbols: str | Iterable[str]) -> TExpr[T]:
"""Return an expression containing terms that involve specific symbols."""

@abstractmethod
def _expand(self) -> TExpr[T]:
pass

@update_expression
def expand(self) -> TExpr[T]:
def expand(self) -> Self:
"""Expand all brackets in the expression."""
return self._expand()

@abstractmethod
def focus(self, symbols: str | Iterable[str]) -> TExpr[T]:
"""Return an expression containing terms that involve specific symbols."""
return replace(self, expression=self._expand(), _previous=(Instruction.Expand, self))

@abstractmethod
def _simplify(self) -> TExpr[T]:
pass

@update_expression
def simplify(self) -> TExpr[T]:
def simplify(self) -> Self:
"""Run the backend `simplify' functionality, if it exists."""
return self._simplify()
return replace(self, expression=self._simplify(), _previous=(Instruction.Simplify, self))

@abstractmethod
def _assume(self, assumption: str | Assumption) -> TExpr[T]:
pass

@update_expression
def assume(self, assumption: str | Assumption) -> TExpr[T]:
def assume(self, assumption: str | Assumption) -> Self:
"""Add an assumption for a symbol."""
expr_with_assumption_applied = self._assume(assumption=assumption)
self.applied_assumptions += (Assumption.from_string(assumption) if isinstance(assumption, str) else assumption,)
return expr_with_assumption_applied
assumption = Assumption.from_string(assumption) if isinstance(assumption, str) else assumption
return replace(
self,
expression=self._assume(assumption=assumption),
assumptions=self.assumptions + (assumption,),
_previous=(str(assumption), self),
)

@update_expression
def reapply_all_assumptions(self) -> TExpr[T]:
def reapply_all_assumptions(self) -> Self:
"""Reapply all previously applied assumptions."""
for assumption in self.applied_assumptions:
self.expression = self.assume(assumption=assumption)
return self.expression
current = self
for assumption in self.assumptions:
current = current.assume(assumption=assumption)
return replace(self, expression=current.expression, _previous=(Instruction.ReapplyAllAssumptions, self))


@dataclass
class ResourceRewriter(Generic[T]):
"""A class for rewriting resource expressions of routines.

Expand All @@ -143,13 +187,13 @@ class ResourceRewriter(Generic[T]):
resource: the resource in the routine we wish to apply rewriting rules to.
"""

_rewriter: Callable[[T | str], ExpressionRewriter[T]]
routine: CompiledRoutine
resource: str
_rewriter: ExpressionRewriter[T]

def __init__(self, routine: CompiledRoutine, resource: str):
self.routine = routine
self.resource = resource
if resource not in self.routine.resources:
raise ValueError(f"Routine {routine.name} has no resource {self.resource}.")
def __post_init__(self):
if self.resource not in self.routine.resources:
raise ValueError(f"Routine {self.routine.name} has no resource {self.resource}.")
self.top_level_expression = cast(T | str, self.routine.resources[self.resource].value)

self.rewriter = self._rewriter(self.top_level_expression)
Expand Down
59 changes: 45 additions & 14 deletions src/bartiq/analysis/_rewriters/sympy_rewriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
"""A rewriter class for SymPy expressions."""

from collections.abc import Iterable
from numbers import Number
from dataclasses import dataclass, field
from numbers import Number as NumberT
from typing import cast

from sympy import Add, Expr, Function, Max, Min, Symbol
from typing_extensions import Self

from bartiq.analysis._rewriters.assumptions import Assumption
from bartiq.analysis._rewriters.expression_rewriter import (
Expand All @@ -28,24 +30,32 @@
from bartiq.symbolics.sympy_backend import SympyBackend
from bartiq.symbolics.sympy_interpreter import Max as CustomMax

_SYMPY_BACKEND = SympyBackend(use_sympy_max=True)


@dataclass
class SympyExpressionRewriter(ExpressionRewriter[Expr]):
"""Rewrite SymPy expressions.

This class accepts a SymPy expression as input, and provides methods for efficient simplification / rewriting of
the input expression.
This class accepts a SymPy expression (or str) as input,
and provides methods for efficient simplification / rewriting of the input expression.

Args:
expression: The sympy expression of interest.
"""

def __init__(self, expression: Expr):
super().__init__(
expression=expression,
backend=SympyBackend(use_sympy_max=True),
)
if not isinstance(expression, Number):
self.expression = cast(Expr, self.expression).replace(CustomMax, Max)
backend: SympyBackend = field(init=False)

def __post_init__(self):
self.backend = _SYMPY_BACKEND
super().__post_init__()
if not isinstance(self.expression, NumberT):
self.expression = cast(Expr, self.expression.replace(CustomMax, Max))

@property
def original(self) -> Self:
"""Return a rewriter with the original expression, and no modifications."""
return type(self)(expression=self._original_expression)

@property
def free_symbols(self) -> set[Expr]:
Expand Down Expand Up @@ -93,7 +103,22 @@ def focus(self, symbols: str | Iterable[str]) -> Expr:
Returns:
A SymPy expression whose terms include the input symbols.
"""
variables = set(map(self.get_symbol, [symbols] if isinstance(symbols, str) else symbols))
symbols = [symbols] if isinstance(symbols, str) else symbols
variables = set()
for sym in symbols:
try:
variables.add(self.get_symbol(sym))
except ValueError:
continue

variables = variables.union(
set(
map(
self.get_symbol,
[key for key, val in self.linked_params.items() if any(sym in val for sym in symbols)],
)
)
)
return sum([term for term in self.individual_terms if not term.free_symbols.isdisjoint(variables)]).collect(
variables
)
Expand Down Expand Up @@ -125,7 +150,7 @@ def all_functions_and_arguments(self) -> set[Expr]:
return set()

def list_arguments_of_function(self, function_name: str) -> list[tuple[Expr, ...] | Expr]:
"""Return a list of arguments X, such that each function_name(x) (for x in X) exists in the expression.
"""Return a list of arguments X, such that each `function_name(x)` (for x in X) exists in the expression.

Args:
function_name: function name to return the arguments of.
Expand All @@ -145,6 +170,7 @@ def _assume(self, assumption: str | Assumption) -> TExpr[Expr]:
if isinstance(self.expression, int | float):
return self.expression
assumption = assumption if isinstance(assumption, Assumption) else Assumption.from_string(assumption)
self.expression = cast(Expr, self.expression)
try:
# If the Symbol exists, replace it with a Symbol that has the correct properties.
reference_symbol = self.get_symbol(symbol_name=assumption.symbol_name)
Expand All @@ -153,7 +179,7 @@ def _assume(self, assumption: str | Assumption) -> TExpr[Expr]:
reference_symbol = replacement
except ValueError:
# If the symbol does _not_ exist, parse the assumption expression.
reference_symbol = self._backend.as_expression(assumption.symbol_name)
reference_symbol = self.backend.as_expression(assumption.symbol_name)

# This is a hacky way to implement assumptions that relate to nonzero values.
replacement_symbol = Symbol(name="__", **assumption.symbol_properties)
Expand All @@ -163,11 +189,16 @@ def _assume(self, assumption: str | Assumption) -> TExpr[Expr]:
return self.expression


@dataclass
class SympyResourceRewriter(ResourceRewriter[Expr]):
"""A class for rewriting sympy resource expressions in routines.

By default, this class only acts on the top level resource. In the future, the ability to propagate
a list of instructions through resources in a routine hierarchy will be made available.
"""

_rewriter = SympyExpressionRewriter
_rewriter: SympyExpressionRewriter = field(init=False)

def __post_init__(self):
self._rewriter = SympyExpressionRewriter
return super().__post_init__()
1 change: 1 addition & 0 deletions src/bartiq/symbolics/sympy_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ def substitute(
restricted_replacements.append((old_sym, new))
except StopIteration:
continue

expr = expr.subs(restricted_replacements)
if functions_map is None:
functions_map = {}
Expand Down
Loading