Skip to content

feat: add methods to apply rewriter instructions to resources in routines#239

Merged
Brendan-Reid1991 merged 285 commits intomainfrom
methods_for_routine_rewriters
Jul 14, 2025
Merged

feat: add methods to apply rewriter instructions to resources in routines#239
Brendan-Reid1991 merged 285 commits intomainfrom
methods_for_routine_rewriters

Conversation

@Brendan-Reid1991
Copy link
Copy Markdown
Contributor

@Brendan-Reid1991 Brendan-Reid1991 commented Jul 8, 2025

Description

  • Did some file restructuring.
    • rewriters/expression.py now contains the base class
    • rewriters/sympy_rewriter.py now contains the SympyExpressionRewriter and the factory method
    • rewriters/resources.py now contains ResourceRewriter
  • Added tests for the routine-level logic.

I also want to settle on names for classes/files in this PR! So make your objections heard here!

Potential issues

  • The ResourceRewriter forwards attribute calls ton the rewriter attribute, and the routine attribute is not modified by instruction calls, only the rewriter is.
    • Calling apply_to_entire_routine pushes all the instructions through every level of the routine, and returns a new CompiledRoutine object.
    • This new object may have some substitutions that are not connected to any other attributes in its definition. The risk is that this object becomes detached from the workflow where it was generated, and essentially becomes a nonsense routine.
  • Usage deviation between ExpressionRewriters and ResourceRewriters
    • Related to the above. Where certain methods on the ExpressionRewriter return a new ExpressionRewriter instance, the same is not true for ResourceRewriters
    • Calling methods that return a new ExpressionRewriter on a ResourceRewriter updates the rewriter attribute, but the class instance remains the same.
    • This creates a divergence in behaviour:
expr = max(0, a)
rewriter = sympy_rewriter(expr)
rewriter.assume("a>0") # a
rewriter.expression == max(0, a) # true

VS

routine = Routine(name='root', ..., resources_values = {"dummy": "max(0,a)}")
resource_rewriter = ResourceRewriter(routine, "dummy")
resource_rewriter.assume("a>0") # a
resource_rewriter.expression == a # true 

That is, the ResourceRewriter instance is updated in-place, whereas the ExpressionRewriter is not.

Please verify that you have completed the following steps

  • I have self-reviewed my code.
  • I have included test cases validating introduced feature/fix.
  • I have updated documentation.

Brendan-Reid1991 and others added 30 commits May 27, 2025 15:25
* docs: Added the list of possible values for `ResourceType` and `PortDirection` on docstring

* docs: Added missing `Raises` sections in existing docstrings

* docs: Improved styling of function segments in docs for easy navigation

* docs: Added clickable reference links to parts of documentations in doc strings

* fix: Fixed the formatting and lint issues

* fix(docs): Fixed docs reference issues on`_compile.py`, `_evaluate.py` & `_routine.py`

* fix: Exposed `SymbolicBackend` through `bartiq.symbolics`

* fix: Exposed `Port` at top level (`bartiq.Port`)

* docs: Adding doc-string for `Port` class

* fix(docs): Added missing Args, and references in modified docs

* fix(docs): Added External references

* fix(docs): Fixed issues of long `Return` descriptions

* fix(docs): Reverting `symbolics` concept markdown content

* fix(docs): Improved styling of overload functions displayed in docs & removed type hints for `postorder_transform`

* fix(docs): Fixed a formatting issue in the module doc-string

* fix(docs): Removed `SymbolicBackend`  from exposing to top-level, and added submodule rendering and extra styling for documentation

* fix(docs): Removed unnecessary doc-strings in each `postorder_transform` overload function

* fix(docs): Extended `module` & `method` annotations in docs
@cla-bot cla-bot bot added the cla-signed label Jul 8, 2025
@Brendan-Reid1991 Brendan-Reid1991 changed the title feat: add methods to apply rewriter instructions to resources in routines. feat: add methods to apply rewriter instructions to resources in routines Jul 8, 2025
Copy link
Copy Markdown
Contributor

@mstechly mstechly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments

return {"name": name, "resources": [{"name": "dummy", "type": "additive", "value": f"max(0, {name})"}]}


b = routine("b")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] IMO it would be clearer and more consistent with rest of tests if we would actually just put an explicit dictionary here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, probably!

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Routine rewriters allow you to rewrite expressions across entire routines."""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] I think it should be Resource rewriters now, no?

Base automatically changed from substitutions to main July 10, 2025 10:35
An error occurred while trying to automatically change base from substitutions to main July 10, 2025 10:35
Comment on lines +98 to +123
routine = deepcopy(self.routine)

def _traverse_routine(routine: CompiledRoutine) -> CompiledRoutine:
"""Recursively traverse the routine, replacing resource values
starting from the lowest level of children."""
for _name, child_routine in routine.children.items():
routine.children[_name] = _traverse_routine(child_routine)
if self.resource in routine.resources and not isinstance(
routine.resource_values[self.resource], (int | float)
):
return replace(
routine,
resources=routine.resources
| {
self.resource: replace(
routine.resources[self.resource],
value=_update_expression(
self.rewriter_factory,
cast(T, routine.resources[self.resource].value),
self.rewriter.history(),
),
)
},
)

return routine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: There is no need for a deepcopy here. Observe that:

  1. Updating of resources does not involve reading from children
  2. Therefore, it doesn't matter if we have updated children or not when we are updating resources.

So, we can just store children and just update them when we do replace.

Even better, we already have similar transformations in other places like preprocessing, and we have a decorator called @postorder_transform that you can use to simplify this piece of code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Comment on lines +6 to +27
root = {
"name": "root",
"children": [
{
"name": "a",
"children": [
{"name": "b", "resources": [{"name": "dummy", "type": "additive", "value": "max(0, b)"}]},
{"name": "c", "resources": [{"name": "dummy", "type": "additive", "value": "max(0, c)"}]},
],
},
{
"name": "x",
"children": [
{"name": "y", "resources": [{"name": "dummy", "type": "additive", "value": "max(0, y)"}]},
{"name": "z", "resources": [{"name": "dummy", "type": "additive", "value": "max(0, z)"}]},
],
},
],
}
compiled = compile_routine(
Routine.from_qref(root, _SYMPY_BACKEND), compilation_flags=CompilationFlags.EXPAND_RESOURCES
).routine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Given some fields are mutable in both routine and compiled routine, I think that it would be much more safe to make those two variables into function-scoped fixtures.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done !

@Brendan-Reid1991 Brendan-Reid1991 merged commit e54a94a into main Jul 14, 2025
9 checks passed
@Brendan-Reid1991 Brendan-Reid1991 deleted the methods_for_routine_rewriters branch July 14, 2025 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

6 participants