Skip to content

Fix scalar * and / mutating the Solution in place (fixes #457) - #461

Open
youdie006 wants to merge 1 commit into
KingsburyLab:mainfrom
youdie006:fix/457-mul-returns-new-solution
Open

Fix scalar * and / mutating the Solution in place (fixes #457)#461
youdie006 wants to merge 1 commit into
KingsburyLab:mainfrom
youdie006:fix/457-mul-returns-new-solution

Conversation

@youdie006

Copy link
Copy Markdown

Fixes #457.

Problem

Standalone sol * factor and sol / factor mutated the original Solution and returned the same object. Root cause: __mul__/__truediv__ did self.volume *= factor; return self, and the volume setter rescales every component in place -- so sol * 2 doubled sol's volume and moles and returned an alias ((sol * 2) is sol was True), instead of a new scaled Solution. Reported by @jjstickel.

Fix

  • __mul__ / __truediv__ now return a new scaled Solution, built via the existing faithful from_dict(as_dict(...)) round-trip and then scaling the copy's volume; the original is left unchanged.
  • __imul__ / __itruediv__ are added to preserve the documented in-place *= / /= scaling behavior (mutate self, return self).
  • __rmul__ is added so factor * sol works (commutativity).

This makes * and / consistent with Python numeric-type semantics, as @rkingsbury requested in the issue thread, while keeping the documented in-place *= / /= behavior intact.

Tests

  • New test_multiplication_returns_new_solution: asserts sol * 2 is not sol, the original is unchanged, the returned Solution has 2x volume/moles, 2 * sol works (rmul), sol / 2 returns a new halved Solution, and sol *= 3 / sol /= 3 still mutate in place and return self.
  • Verified red -> green (pre-fix the new test fails at assert doubled is not sol) and the full tests/test_solution.py suite passes with no regressions (test_arithmetic_and_copy included).

This contribution was prepared with AI assistance and reviewed by me before submission.

Standalone sol * factor and sol / factor mutated the original Solution and
returned the same object. __mul__/__truediv__ did self.volume *= factor;
return self, and the volume setter rescales every component in place, so sol * 2
doubled sol's volume and moles and returned an alias ((sol * 2) is sol was True)
instead of a new scaled Solution.

__mul__/__truediv__ now return a new scaled Solution, built via the existing
faithful from_dict(as_dict(...)) round-trip and then scaling the copy's volume;
the original is left unchanged. __imul__/__itruediv__ are added to preserve the
documented in-place *= / /= scaling behavior (mutate self, return self), and
__rmul__ is added so factor * sol works. This makes * and / consistent with
Python numeric-type semantics while keeping in-place *= / /= intact.

Fixes KingsburyLab#457.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

scalar multiplication of a solution modifies the solution in place

1 participant