Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove o(n^2) operation from NumberedObjectCollection #563

Merged
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
13367f8
Avoided O(N^2) for a bulk import.
MicahGale Oct 2, 2024
902eeba
Merge branch 'weakref' into 556-remove-on2-operation-from-numberedobj…
MicahGale Oct 2, 2024
1b3cd95
Moved checking to trusting the number cache.
MicahGale Oct 2, 2024
9a73a94
Simplified code to rely on check_numbers.
MicahGale Oct 2, 2024
eb92877
Allowed objects to change their number in the num cache.
MicahGale Oct 2, 2024
66939ac
Moved all number validation to Abstract class, and invoked update
MicahGale Oct 2, 2024
82a85e1
Made code more fault tol.
MicahGale Oct 2, 2024
ed92d06
Updated update method to use num_cache.
MicahGale Oct 3, 2024
4fe23a6
Guarded against clones stealing numbers.
MicahGale Oct 3, 2024
6a2e7b5
Merge branch 'weakref' into 556-remove-on2-operation-from-numberedobj…
MicahGale Oct 3, 2024
e88fe76
Fixed object map.
MicahGale Oct 3, 2024
a8606c8
Made dict getter more fault tolerant.
MicahGale Oct 3, 2024
31e7ad6
Handled case of looking up for parent class.
MicahGale Oct 3, 2024
d714258
Added #556 to changelog.
MicahGale Oct 3, 2024
7cb9d16
Changed error to correct one.
MicahGale Oct 3, 2024
23b720b
Merge branch 'develop' into 556-remove-on2-operation-from-numberedobj…
MicahGale Oct 8, 2024
094cbe5
Merge branch 'weakref' into 556-remove-on2-operation-from-numberedobj…
MicahGale Oct 8, 2024
5439371
Merge branch 'develop' into 556-remove-on2-operation-from-numberedobj…
MicahGale Oct 8, 2024
8c0fc04
Merge branch 'develop' into 556-remove-on2-operation-from-numberedobj…
MicahGale Oct 10, 2024
eade9cd
Made code more succinct.
MicahGale Oct 10, 2024
2384166
Filled out doc strings for update_number.
MicahGale Oct 10, 2024
eccb881
Added removal of python 3.8 to the changelog.
MicahGale Oct 12, 2024
fb719fd
Tried to give coveralls permission to show results.
MicahGale Oct 13, 2024
479b581
Restricted permissions.
MicahGale Oct 13, 2024
6719084
Gave coveralls actual access to permissions.
MicahGale Oct 13, 2024
9fe1c47
Revert "Restricted permissions."
MicahGale Oct 13, 2024
af9c8ca
Merge branch 'develop' into 556-remove-on2-operation-from-numberedobj…
MicahGale Oct 15, 2024
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
1 change: 1 addition & 0 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ MontePy Changelog
**Performance Improvement**

* Fixed cyclic memory reference that lead to memory leak in ``copy.deepcopy`` (:issue:`514`).
* Fixed O(N<sup>2</sup>) operation in how append works for object collections like Cells (:issue:`556`).

**Bug Fixes**

Expand Down
16 changes: 0 additions & 16 deletions montepy/cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,6 @@
from montepy.utilities import *


def _number_validator(self, number):
if number <= 0:
raise ValueError("number must be > 0")
if self._problem:
self._problem.cells.check_number(number)


def _link_geometry_to_cell(self, geom):
geom._cell = self
geom._add_new_children_to_cell(geom)
Expand Down Expand Up @@ -313,15 +306,6 @@ def old_number(self):
"""
pass

@make_prop_val_node("_number", int, validator=_number_validator)
def number(self):
"""
The current cell number that will be written out to a new input.

:rtype: int
"""
pass

@make_prop_pointer("_material", (Material, type(None)), deletable=True)
def material(self):
"""
Expand Down
16 changes: 0 additions & 16 deletions montepy/data_inputs/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@
import warnings


def _number_validator(self, number):
if number <= 0:
raise ValueError("number must be > 0")
if self._problem:
self._problem.materials.check_number(number)


class Material(data_input.DataInputAbstract, Numbered_MCNP_Object):
"""
A class to represent an MCNP material.
Expand Down Expand Up @@ -85,15 +78,6 @@ def old_number(self):
"""
pass

@make_prop_val_node("_number", int, validator=_number_validator)
def number(self):
"""
The number to use to identify the material by

:rtype: int
"""
pass

@property
def is_atom_fraction(self):
"""
Expand Down
15 changes: 0 additions & 15 deletions montepy/data_inputs/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@
import re


def _enforce_number(self, val):
if val <= 0:
raise ValueError(f"Transform number must be > 0. {val} given.")


class Transform(data_input.DataInputAbstract, Numbered_MCNP_Object):
"""
Input to represent a transform input (TR).
Expand Down Expand Up @@ -109,16 +104,6 @@ def is_in_degrees(self):
"""
pass

@make_prop_val_node("_number", (int, float), int, _enforce_number)
def number(self):
"""
The transform number for this transform

:rtype: int

"""
pass

@make_prop_val_node("_old_number")
def old_number(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion montepy/mcnp_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class MCNP_Problem:
surface.Surface: Surfaces,
Material: Materials,
transform.Transform: Transforms,
montepy.universe: Universes,
montepy.universe.Universe: Universes,
}

def __init__(self, destination):
Expand Down
26 changes: 24 additions & 2 deletions montepy/numbered_mcnp_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,33 @@
from montepy.errors import NumberConflictError
from montepy.mcnp_object import MCNP_Object
import montepy
from montepy.utilities import *


def _number_validator(self, number):
if number < 0:
raise ValueError("number must be >= 0")
if self._problem:
obj_map = montepy.MCNP_Problem._NUMBERED_OBJ_MAP
try:
collection_type = obj_map[type(self)]
except KeyError as e:
found = False
for obj_class in obj_map:
if isinstance(self, obj_class):
collection_type = obj_map[obj_class]
found = True
break
if not found:
raise e
collection = getattr(self._problem, collection_type.__name__.lower())
collection.check_number(number)
collection._update_number(self.number, number, self)


class Numbered_MCNP_Object(MCNP_Object):
@property
@abstractmethod

@make_prop_val_node("_number", int, validator=_number_validator)
def number(self):
"""
The current number of the object that will be written out to a new input.
Expand Down
73 changes: 33 additions & 40 deletions montepy/numbered_object_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ def numbers(self):

:rtype: generator
"""
self.__num_cache
for obj in self._objects:
# update cache every time we go through all objects
self.__num_cache[obj.number] = obj
Expand All @@ -128,11 +127,27 @@ def check_number(self, number):
"""
if not isinstance(number, int):
raise TypeError("The number must be an int")
if number in self.numbers:
conflict = False
# only can trust cache if being
if self._problem:
if number in self.__num_cache:
conflict = True
else:
if number in self.numbers:
conflict = True
if conflict:
raise NumberConflictError(
f"Number {number} is already in use for the collection: {type(self)} by {self[number]}"
)

def _update_number(self, old_num, new_num, obj):
""" """
MicahGale marked this conversation as resolved.
Show resolved Hide resolved
# don't update numbers you don't own
if self.__num_cache.get(old_num, None) is not obj:
return
self.__num_cache.pop(old_num, None)
self.__num_cache[new_num] = obj

@property
def objects(self):
"""
Expand Down Expand Up @@ -177,22 +192,25 @@ def extend(self, other_list):
"""
if not isinstance(other_list, (list, type(self))):
raise TypeError("The extending list must be a list")
if self._problem:
nums = set(self.__num_cache.keys())
MicahGale marked this conversation as resolved.
Show resolved Hide resolved
else:
nums = set(self.numbers)
for obj in other_list:
if not isinstance(obj, self._obj_class):
raise TypeError(
"The object in the list {obj} is not of type: {self._obj_class}"
)
if obj.number in self.numbers:
if obj.number in nums:
raise NumberConflictError(
(
f"When adding to {type(self)} there was a number collision due to "
f"adding {obj} which conflicts with {self[obj.number]}"
)
)
# if this number is a ghost; remove it.
else:
self.__num_cache.pop(obj.number, None)
nums.add(obj.number)
self._objects.extend(other_list)
self.__num_cache.update({obj.number: obj for obj in other_list})
if self._problem:
for obj in other_list:
obj.link_to_problem(self._problem)
Expand Down Expand Up @@ -295,15 +313,8 @@ def append(self, obj):
"""
if not isinstance(obj, self._obj_class):
raise TypeError(f"object being appended must be of type: {self._obj_class}")
if obj.number in self.numbers:
raise NumberConflictError(
(
"There was a numbering conflict when attempting to add "
f"{obj} to {type(self)}. Conflict was with {self[obj.number]}"
)
)
else:
self.__num_cache[obj.number] = obj
self.check_number(obj.number)
self.__num_cache[obj.number] = obj
self._objects.append(obj)
if self._problem:
obj.link_to_problem(self._problem)
Expand Down Expand Up @@ -368,8 +379,12 @@ def request_number(self, start_num=None, step=None):
if step is None:
step = self.step
number = start_num
while number in self.numbers:
number += step
while True:
try:
self.check_number(number)
break
except NumberConflictError:
number += step
return number

def next_number(self, step=1):
Expand Down Expand Up @@ -450,29 +465,7 @@ def __len__(self):
return len(self._objects)

def __iadd__(self, other):
if not isinstance(other, (type(self), list)):
raise TypeError(f"Appended item must be a list or of type {type(self)}")
for obj in other:
if not isinstance(obj, self._obj_class):
raise TypeError(
f"Appended object {obj} must be of type: {self._obj_class}"
)
if isinstance(other, type(self)):
other_list = other.objects
else:
other_list = other
for obj in other_list:
if obj.number in self.numbers:
raise NumberConflictError(
(
"There was a numbering conflict when attempting to add "
f"{obj} to {type(self)}. Conflict was with {self[obj.number]}"
)
)
else:
self.__num_cache[obj.number] = obj
for obj in other_list:
self.append(obj)
self.extend(other)
return self

def __contains__(self, other):
Expand Down
14 changes: 0 additions & 14 deletions montepy/surfaces/surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@
import re


def _enforce_numbers(self, value):
if value <= 0:
raise ValueError(f"The number be greater than 0; {value} given.")


class Surface(Numbered_MCNP_Object):
"""
Object to hold a single MCNP surface
Expand Down Expand Up @@ -194,15 +189,6 @@ def old_number(self):
"""
pass

@make_prop_val_node("_number", int, validator=_enforce_numbers)
def number(self):
"""
The surface number to use.

:rtype: int
"""
pass

@property
def cells(self):
"""
Expand Down
22 changes: 1 addition & 21 deletions montepy/universe.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self, number):
raise TypeError("number must be int")
if number < 0:
raise ValueError(f"Universe number must be ≥ 0. {number} given.")
self._number = number
self._number = montepy.input_parser.syntax_node.ValueNode(number, int)

class Parser:
def parse(self, token_gen, input):
Expand Down Expand Up @@ -62,26 +62,6 @@ def claim(self, cells):
for cell in cells:
cell.universe = self

@property
def number(self):
"""
The number for this Universe.

Universe 0 is protected, and a Universe cannot be set 0,
if it is not already Universe 0.
"""
return self._number

@number.setter
def number(self, number):
if not isinstance(number, int):
raise TypeError("number must be int")
if number <= 0:
raise ValueError("Universe number must be > 0")
if self._problem:
self._problem.universes.check_number(number)
self._number = number

@property
def old_number(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,7 @@ def test_universe_number_collision():
with pytest.raises(montepy.errors.NumberConflictError):
problem.universes[0].number = 350

with pytest.raises(ValueError):
with pytest.raises(montepy.errors.NumberConflictError):
problem.universes[350].number = 0


Expand Down