1111import weakref
1212from typing import Any
1313from typing import Dict
14+ from typing import List
1415from typing import Optional
1516from typing import Union
1617
@@ -36,14 +37,6 @@ class Parameter(DescriptorNumber):
3637 # We copy the parent's _REDIRECT and modify it to avoid altering the parent's class dict
3738 _REDIRECT = DescriptorNumber ._REDIRECT .copy ()
3839 _REDIRECT ['callback' ] = None
39- # Skip these attributes during normal serialization as they are handled specially
40- _REDIRECT ['_dependency_interpreter' ] = None
41- _REDIRECT ['_clean_dependency_string' ] = None
42- # Skip the new serialization parameters - they'll be handled by _convert_to_dict
43- _REDIRECT ['_dependency_string' ] = None
44- _REDIRECT ['_dependency_map_unique_names' ] = None
45- _REDIRECT ['_dependency_map_dependency_ids' ] = None
46- _REDIRECT ['__dependency_id' ] = None
4740
4841 def __init__ (
4942 self ,
@@ -84,11 +77,8 @@ def __init__(
8477 """ # noqa: E501
8578 # Extract and ignore serialization-specific fields from kwargs
8679 kwargs .pop ('_dependency_string' , None )
87- kwargs .pop ('_dependency_map_unique_names' , None )
8880 kwargs .pop ('_dependency_map_dependency_ids' , None )
8981 kwargs .pop ('_independent' , None )
90- # Extract dependency_id if provided during deserialization
91- provided_dependency_id = kwargs .pop ('__dependency_id' , None )
9282
9383 if not isinstance (min , numbers .Number ):
9484 raise TypeError ('`min` must be a number' )
@@ -119,6 +109,7 @@ def __init__(
119109 url = url ,
120110 display_name = display_name ,
121111 parent = parent ,
112+ ** kwargs , # Additional keyword arguments (used for (de)serialization)
122113 )
123114
124115 self ._callback = callback # Callback is used by interface to link to model
@@ -128,13 +119,6 @@ def __init__(
128119 # Create additional fitting elements
129120 self ._initial_scalar = copy .deepcopy (self ._scalar )
130121
131- # Generate unique dependency ID for serialization/deserialization
132- # Use provided dependency_id if available (during deserialization), otherwise generate new one
133- if provided_dependency_id is not None :
134- self .__dependency_id = provided_dependency_id
135- else :
136- import uuid
137- self .__dependency_id = str (uuid .uuid4 ())
138122
139123 @classmethod
140124 def from_dependency (cls , name : str , dependency_expression : str , dependency_map : Optional [dict ] = None , ** kwargs ) -> Parameter : # noqa: E501
@@ -147,15 +131,15 @@ def from_dependency(cls, name: str, dependency_expression: str, dependency_map:
147131 :param kwargs: Additional keyword arguments to pass to the Parameter constructor.
148132 :return: A new dependent Parameter object.
149133 """ # noqa: E501
150- # Set default values for required parameters if not provided in kwargs
134+ # Set default values for required parameters for the constructor, they get overwritten by the dependency anyways
151135 default_kwargs = {
152136 'value' : 0.0 ,
153137 'unit' : '' ,
154138 'variance' : 0.0 ,
155139 'min' : - np .inf ,
156140 'max' : np .inf
157141 }
158- # Update with user-provided kwargs, giving precedence to user values
142+ # Update with user-provided kwargs, to avoid errors.
159143 default_kwargs .update (kwargs )
160144 parameter = cls (name = name , ** default_kwargs )
161145 parameter .make_dependent_on (dependency_expression = dependency_expression , dependency_map = dependency_map )
@@ -331,15 +315,6 @@ def dependency_map(self) -> Dict[str, DescriptorNumber]:
331315 def dependency_map (self , new_map : Dict [str , DescriptorNumber ]) -> None :
332316 raise AttributeError ('Dependency map is read-only. Use `make_dependent_on` to change the dependency map.' )
333317
334- @property
335- def dependency_id (self ) -> str :
336- """
337- Get the unique dependency ID of this parameter used for serialization.
338-
339- :return: The dependency ID of this parameter.
340- """
341- return self .__dependency_id
342-
343318 @property
344319 def value_no_call_back (self ) -> numbers .Number :
345320 """
@@ -553,6 +528,26 @@ def free(self) -> bool:
553528 def free (self , value : bool ) -> None :
554529 self .fixed = not value
555530
531+ def as_dict (self , skip : Optional [List [str ]] = None ) -> Dict [str , Any ]:
532+ """ Overwrite the as_dict method to handle dependency information. """
533+ raw_dict = super ().as_dict (skip = skip )
534+
535+
536+ # Add dependency information for dependent parameters
537+ if not self ._independent :
538+ # Save the dependency expression
539+ raw_dict ['_dependency_string' ] = self ._clean_dependency_string
540+
541+ # Mark that this parameter is dependent
542+ raw_dict ['_independent' ] = self ._independent
543+
544+ # Convert dependency_map to use dependency_ids
545+ raw_dict ['_dependency_map_dependency_ids' ] = {}
546+ for key , obj in self ._dependency_map .items ():
547+ raw_dict ['_dependency_map_dependency_ids' ][key ] = obj ._DescriptorNumber__dependency_id
548+
549+ return raw_dict
550+
556551 def _revert_dependency (self , skip_detach = False ) -> None :
557552 """
558553 Revert the dependency to the old dependency. This is used when an error is raised during setting the dependency.
@@ -595,63 +590,26 @@ def _process_dependency_unique_names(self, dependency_expression: str):
595590 raise ValueError (f'The object with unique_name { stripped_name } is not a Parameter or DescriptorNumber. Please check your dependency expression.' ) # noqa: E501
596591 self ._clean_dependency_string = clean_dependency_string
597592
598- def _convert_to_dict (self , d : dict , encoder , skip = None , ** kwargs ) -> dict :
599- """Custom serialization to handle parameter dependencies."""
600- if skip is None :
601- skip = []
602-
603- # Add dependency information for dependent parameters
604- if not self ._independent :
605- # Save the dependency expression
606- d ['_dependency_string' ] = self ._dependency_string
607-
608- # Convert dependency_map to use dependency_ids (preferred) and unique_names (fallback)
609- d ['_dependency_map_dependency_ids' ] = {}
610- d ['_dependency_map_unique_names' ] = {}
611-
612- for key , dep_obj in self ._dependency_map .items ():
613- # Store dependency_id if available (more reliable)
614- if hasattr (dep_obj , '__dependency_id' ):
615- d ['_dependency_map_dependency_ids' ][key ] = dep_obj .__dependency_id
616- # Also store unique_name as fallback
617- if hasattr (dep_obj , 'unique_name' ):
618- d ['_dependency_map_unique_names' ][key ] = dep_obj .unique_name
619- else :
620- # This is quite impossible - throw an error
621- raise ValueError (f'The object with unique_name { key } does not have a unique_name attribute.' )
622-
623- # Always include dependency_id for this parameter
624- d ['__dependency_id' ] = self .__dependency_id
625-
626- # Mark that this parameter is dependent
627- d ['_independent' ] = self ._independent
628-
629- return d
630-
631- @classmethod
593+ @classmethod
632594 def from_dict (cls , obj_dict : dict ) -> 'Parameter' :
633595 """
634596 Custom deserialization to handle parameter dependencies.
635597 Override the parent method to handle dependency information.
636598 """
637599 # Extract dependency information before creating the parameter
638- d = obj_dict .copy () # Don't modify the original dict
639- dependency_string = d .pop ('_dependency_string' , None )
640- dependency_map_unique_names = d .pop ('_dependency_map_unique_names' , None )
641- dependency_map_dependency_ids = d .pop ('_dependency_map_dependency_ids' , None )
642- is_independent = d .pop ('_independent' , True )
643- # Note: Keep __dependency_id in the dict so it gets passed to __init__
600+ raw_dict = obj_dict .copy () # Don't modify the original dict
601+ dependency_string = raw_dict .pop ('_dependency_string' , None )
602+ dependency_map_dependency_ids = raw_dict .pop ('_dependency_map_dependency_ids' , None )
603+ is_independent = raw_dict .pop ('_independent' , True )
604+ # Note: Keep _dependency_id in the dict so it gets passed to __init__
644605
645606 # Create the parameter using the base class method (dependency_id is now handled in __init__)
646- param = super ().from_dict (d )
607+ param = super ().from_dict (raw_dict )
647608
648609 # Store dependency information for later resolution
649- if not is_independent and dependency_string is not None :
610+ if not is_independent :
650611 param ._pending_dependency_string = dependency_string
651- if dependency_map_dependency_ids :
652- param ._pending_dependency_map_dependency_ids = dependency_map_dependency_ids
653- if dependency_map_unique_names :
654- param ._pending_dependency_map_unique_names = dependency_map_unique_names
612+ param ._pending_dependency_map_dependency_ids = dependency_map_dependency_ids
655613 # Keep parameter as independent initially - will be made dependent after all objects are loaded
656614 param ._independent = True
657615
@@ -995,13 +953,12 @@ def resolve_pending_dependencies(self) -> None:
995953 """Resolve pending dependencies after deserialization.
996954
997955 This method should be called after all parameters have been deserialized
998- to establish dependency relationships using dependency_ids or unique_names as fallback .
956+ to establish dependency relationships using dependency_ids.
999957 """
1000958 if hasattr (self , '_pending_dependency_string' ):
1001959 dependency_string = self ._pending_dependency_string
1002960 dependency_map = {}
1003961
1004- # Try dependency IDs first (more reliable)
1005962 if hasattr (self , '_pending_dependency_map_dependency_ids' ):
1006963 dependency_map_dependency_ids = self ._pending_dependency_map_dependency_ids
1007964
@@ -1013,22 +970,6 @@ def resolve_pending_dependencies(self) -> None:
1013970 else :
1014971 raise ValueError (f"Cannot find parameter with dependency_id '{ dependency_id } '" )
1015972
1016- # Fallback to unique_names if dependency IDs not available or incomplete
1017- if hasattr (self , '_pending_dependency_map_unique_names' ):
1018- dependency_map_unique_names = self ._pending_dependency_map_unique_names
1019-
1020- for key , unique_name in dependency_map_unique_names .items ():
1021- if key not in dependency_map : # Only add if not already resolved by dependency_id
1022- try :
1023- # Look up the parameter by unique_name in the global map
1024- dep_obj = self ._global_object .map .get_item_by_key (unique_name )
1025- if dep_obj is not None :
1026- dependency_map [key ] = dep_obj
1027- else :
1028- raise ValueError (f"Cannot find parameter with unique_name '{ unique_name } '" )
1029- except Exception as e :
1030- raise ValueError (f"Error resolving dependency '{ key } ' -> '{ unique_name } ': { e } " )
1031-
1032973 # Establish the dependency relationship
1033974 try :
1034975 self .make_dependent_on (dependency_expression = dependency_string , dependency_map = dependency_map )
@@ -1037,14 +978,13 @@ def resolve_pending_dependencies(self) -> None:
1037978
1038979 # Clean up temporary attributes
1039980 delattr (self , '_pending_dependency_string' )
1040- if hasattr (self , '_pending_dependency_map_dependency_ids' ):
1041- delattr (self , '_pending_dependency_map_dependency_ids' )
1042- if hasattr (self , '_pending_dependency_map_unique_names' ):
1043- delattr (self , '_pending_dependency_map_unique_names' )
981+ delattr (self , '_pending_dependency_map_dependency_ids' )
1044982
1045- def _find_parameter_by_dependency_id (self , dependency_id : str ) -> Optional ['Parameter ' ]:
983+ def _find_parameter_by_dependency_id (self , dependency_id : str ) -> Optional ['DescriptorNumber ' ]:
1046984 """Find a parameter by its dependency_id from all parameters in the global map."""
1047985 for obj in self ._global_object .map ._store .values ():
1048- if isinstance (obj , Parameter ) and hasattr (obj , '__dependency_id' ) and obj .__dependency_id == dependency_id :
1049- return obj
986+ if isinstance (obj , DescriptorNumber ) and hasattr (obj , '_DescriptorNumber__dependency_id' ):
987+ if obj ._DescriptorNumber__dependency_id == dependency_id :
988+ return obj
1050989 return None
990+
0 commit comments