topologicpy 0.8.95__py3-none-any.whl → 0.8.97__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
topologicpy/Dictionary.py CHANGED
@@ -46,6 +46,139 @@ class Dictionary():
46
46
  dictionaries.append(Dictionary.ByKeysValues(keys, values))
47
47
  return dictionaries
48
48
  '''
49
+
50
+ @staticmethod
51
+ def _ConvertAttribute(attr):
52
+ """
53
+ Convert the found attribute into the proper value
54
+ """
55
+ from topologicpy.Topology import Topology
56
+ import json
57
+
58
+ def is_json_string(input_string):
59
+ """
60
+ Check if the input string is a valid JSON string.
61
+ """
62
+ try:
63
+ json.loads(input_string)
64
+ return True
65
+ except json.JSONDecodeError:
66
+ return False
67
+
68
+ def json_to_dict(json_string):
69
+ """
70
+ Convert a JSON-formatted string to a Python dictionary.
71
+ """
72
+ return json.loads(json_string)
73
+
74
+ if isinstance(attr, IntAttribute):
75
+ return (attr.IntValue())
76
+ elif isinstance(attr, DoubleAttribute):
77
+ return (attr.DoubleValue())
78
+ elif isinstance(attr, StringAttribute):
79
+ temp_value = attr.StringValue()
80
+ topologies = None
81
+ try:
82
+ if is_json_string(temp_value):
83
+ topologies = Topology.ByJSONString(temp_value)
84
+ else:
85
+ topologies = None
86
+ except:
87
+ topologies = None
88
+ if isinstance(topologies, list):
89
+ if len(topologies) == 0:
90
+ topologies = None
91
+ if temp_value == "__NONE__":
92
+ return None
93
+ elif Topology.IsInstance(topologies, "Topology"):
94
+ return topologies
95
+ elif isinstance(topologies, list):
96
+ if len(topologies) > 1:
97
+ return topologies
98
+ elif len(topologies) == 1:
99
+ return topologies[0]
100
+ elif is_json_string(temp_value):
101
+ ret_value = json_to_dict(temp_value)
102
+ return ret_value
103
+ else:
104
+ return (temp_value)
105
+ elif isinstance(attr, ListAttribute):
106
+ return (Dictionary.ListAttributeValues(attr))
107
+ elif isinstance(attr, float) or isinstance(attr, int):
108
+ return attr
109
+ elif isinstance(attr, str):
110
+ if attr == "__NONE__":
111
+ return None
112
+ topologies = None
113
+ try:
114
+ if is_json_string(attr):
115
+ topologies = Topology.ByJSONString(attr)
116
+ else:
117
+ topologies = None
118
+ except:
119
+ topologies = None
120
+ if isinstance(topologies, list):
121
+ if len(topologies) > 1:
122
+ return topologies
123
+ elif len(topologies) == 1:
124
+ return topologies[0]
125
+ elif is_json_string(attr):
126
+ return json_to_dict(attr)
127
+ else:
128
+ return (attr)
129
+ elif isinstance(attr, tuple):
130
+ return Dictionary.ListAttributeValues([Dictionary._ConvertAttribute(x) for x in list(attr)])
131
+ elif isinstance(attr, list):
132
+ return Dictionary.ListAttributeValues([Dictionary._ConvertAttribute(x) for x in attr])
133
+ elif isinstance(attr, dict):
134
+ return attr
135
+ else:
136
+ return None
137
+
138
+ @staticmethod
139
+ def _ConvertValue(value):
140
+ """
141
+ Converts the input value to the proper attribute
142
+ """
143
+ from topologicpy.Topology import Topology
144
+ import json
145
+
146
+ def dict_to_json(py_dict):
147
+ """
148
+ Convert a Python dictionary to a JSON-formatted string.
149
+ """
150
+ return json.dumps(py_dict, indent=2)
151
+
152
+ attr = topologic.StringAttribute("__NONE__") # Hook to Core
153
+ if value == None:
154
+ attr = topologic.StringAttribute("__NONE__") # Hook to Core
155
+ elif isinstance(value, bool):
156
+ if value == False:
157
+ attr = topologic.IntAttribute(0) # Hook to Core
158
+ else:
159
+ attr = topologic.IntAttribute(1) # Hook to Core
160
+ elif isinstance(value, int):
161
+ attr = topologic.IntAttribute(value) # Hook to Core
162
+ elif isinstance(value, float):
163
+ attr = topologic.DoubleAttribute(value) # Hook to Core
164
+ elif Topology.IsInstance(value, "Topology"):
165
+ str_value = Topology.JSONString(value)
166
+ attr = topologic.StringAttribute(str_value) # Hook to Core
167
+ elif isinstance(value, dict):
168
+ str_value = dict_to_json(value)
169
+ attr = topologic.StringAttribute(str_value) # Hook to Core
170
+ elif isinstance(value, str):
171
+ attr = topologic.StringAttribute(value) # Hook to Core
172
+ elif isinstance(value, tuple):
173
+ l = [Dictionary._ConvertValue(v) for v in list(value)]
174
+ attr = topologic.ListAttribute(l) # Hook to Core
175
+ elif isinstance(value, list):
176
+ l = [Dictionary._ConvertValue(v) for v in value]
177
+ attr = topologic.ListAttribute(l) # Hook to Core
178
+ else:
179
+ attr = topologic.StringAttribute("__NONE__") # Hook to Core
180
+ return attr
181
+
49
182
  @staticmethod
50
183
  def AdjacencyDictionary(topology, subTopologyType: str = None, labelKey: str = None, weightKey: str = None, includeWeights: bool = False, mantissa: int = 6, silent: bool = False):
51
184
  """
@@ -165,7 +298,134 @@ class Dictionary():
165
298
  d = Dictionary.RemoveKey(d, labelKey)
166
299
  subtopology = Topology.SetDictionary(subtopology, d)
167
300
  return adjDict
168
-
301
+
302
+ @staticmethod
303
+ def BooleanDictionariesByKey(dictionariesA,
304
+ dictionariesB,
305
+ key: str,
306
+ operation: str = "union",
307
+ exclusive: bool = True,
308
+ silent: bool = False):
309
+ """
310
+ Booleans the keys/values of the dictionaries in the second list on the
311
+ dictionaries in the first list based on a shared dictionary key/value and the boolean operation.
312
+
313
+ If a dictionary in the first list and a dictionary in second list have the same value
314
+ for the given dictionary key, the dictionary from the second list is imprinted the dictionary
315
+ in the first list.
316
+
317
+ Parameters
318
+ ----------
319
+ dictionariesA : list[topologic_core.Dictionary]
320
+ Target list of dictionaries to be updated.
321
+ dictionariesB : list[topologic_core.Dictionary]
322
+ Source list of dictionaries providing values.
323
+ key : str
324
+ Dictionary key used to match dictionaries.
325
+ operation : str , optional
326
+ the type of desired boolean operation. Options are:
327
+ - "union" / "merge"
328
+ - "difference"
329
+ - "intersection"
330
+ - "SymmetricDifference" / "SymDif" / "XOR"
331
+ - "Impose"
332
+ - "Imprint"
333
+ The options are case-insensitive. Default is "union"
334
+ exclusive: bool , optional
335
+ If set to True, only the first matching dictionary in the second list is applied.
336
+ silent : bool , optional
337
+ If set to True, error and warning messages are suppressed. Default is False.
338
+
339
+ Returns
340
+ -------
341
+ list[topologic_core.Dictionary]
342
+ The updated first list of dictionaries
343
+ """
344
+
345
+ from topologicpy.Topology import Topology
346
+ from topologicpy.Dictionary import Dictionary
347
+
348
+ dictionariesA = [d for d in dictionariesA if Topology.IsInstance(d, "Dictionary")]
349
+ if len(dictionariesA) < 1:
350
+ if not silent:
351
+ print("Dictionary.BooleanDictionariesByKey - Error: The dictionariesA input parameter does not contain any valid dictionaries. Returning None.")
352
+ return None
353
+
354
+ dictionariesB = [d for d in dictionariesB if Topology.IsInstance(d, "Dictionary")]
355
+ if len(dictionariesB) < 1:
356
+ if not silent:
357
+ print("Dictionary.BooleanDictionariesByKey - Error: The dictionariesB input parameter does not contain any valid dictionaries. Returning None.")
358
+ return None
359
+
360
+ dictionariesC = []
361
+ op = operation.lower()
362
+ if not op in ["union", "merge", "difference", "intersection", "symmetricdifference", "symdif", "xor", "impose", "imprint"]:
363
+ if not silent:
364
+ print("Dictionary.BooleanDictionariesByKey - Error: Unrecognized boolean operation. Returning None.")
365
+ return None
366
+ # Imprint dictionariesB unto dictionariesA
367
+ if exclusive:
368
+ lookup = {}
369
+ for d in dictionariesB:
370
+ if d:
371
+ v = Dictionary.ValueAtKey(d, key)
372
+ lookup[v] = d
373
+ for dA in dictionariesA:
374
+ if not dA:
375
+ continue
376
+ vA = Dictionary.ValueAtKey(dA, key, None)
377
+ dB = lookup.get(vA, None)
378
+ if dB:
379
+ if op in ["union", "merge"]:
380
+ dA = Dictionary.Union(dA, dB)
381
+ elif op == "difference":
382
+ dA = Dictionary.Difference(dA, dB)
383
+ elif op == "intersection":
384
+ dA = Dictionary.Intersection(dA, dB)
385
+ elif op in ["symmetricdifference", "symdif", "xor"]:
386
+ dA = Dictionary.SymmetricDifference(dA, dB)
387
+ elif op == "impose":
388
+ dA = Dictionary.Impose(dA, dB)
389
+ elif op == "imprint":
390
+ dA = Dictionary.Imprint(dA, dB)
391
+ else:
392
+ continue
393
+ dictionariesC.append(dA)
394
+ else:
395
+ if not silent:
396
+ print(f"Dictionary.BooleanDictionariesByKey - Warning: No match found for {vA}. Assigning an empty dictionary.")
397
+ dictionariesC.append(Dictionary.ByKeysValues([],[]))
398
+ else:
399
+ for dA in dictionariesA:
400
+ if not dA:
401
+ continue
402
+ vA = Dictionary.ValueAtKey(dA, key, None)
403
+ if vA:
404
+ for dB in dictionariesB:
405
+ if not dB:
406
+ continue
407
+ vB = Dictionary.ValueAtKey(dB, key, None)
408
+ if not vB:
409
+ continue
410
+ if vA == vB:
411
+ if op in ["union", "merge"]:
412
+ dA = Dictionary.Union(dA, dB)
413
+ elif op == "difference":
414
+ dA = Dictionary.Difference(dA, dB)
415
+ elif op == "intersection":
416
+ dA = Dictionary.Intersection(dA, dB)
417
+ elif op in ["symmetricdifference", "symdif", "xor"]:
418
+ dA = Dictionary.SymmetricDifference(dA, dB)
419
+ elif op == "impose":
420
+ dA = Dictionary.Impose(dA, dB)
421
+ elif op == "imprint":
422
+ dA = Dictionary.Imprint(dA, dB)
423
+ else:
424
+ continue
425
+ dictionariesC.append(dA)
426
+
427
+ return dictionariesC
428
+
169
429
  @staticmethod
170
430
  def ByKeyValue(key, value, silent: bool = False):
171
431
  """
@@ -192,51 +452,6 @@ class Dictionary():
192
452
  return None
193
453
  return Dictionary.ByKeysValues([key], [value], silent=silent)
194
454
 
195
-
196
- @staticmethod
197
- def _ConvertValue(value):
198
- """
199
- Converts the input value to the proper attribute
200
- """
201
- from topologicpy.Topology import Topology
202
- import json
203
-
204
- def dict_to_json(py_dict):
205
- """
206
- Convert a Python dictionary to a JSON-formatted string.
207
- """
208
- return json.dumps(py_dict, indent=2)
209
-
210
- attr = topologic.StringAttribute("__NONE__") # Hook to Core
211
- if value == None:
212
- attr = topologic.StringAttribute("__NONE__") # Hook to Core
213
- elif isinstance(value, bool):
214
- if value == False:
215
- attr = topologic.IntAttribute(0) # Hook to Core
216
- else:
217
- attr = topologic.IntAttribute(1) # Hook to Core
218
- elif isinstance(value, int):
219
- attr = topologic.IntAttribute(value) # Hook to Core
220
- elif isinstance(value, float):
221
- attr = topologic.DoubleAttribute(value) # Hook to Core
222
- elif Topology.IsInstance(value, "Topology"):
223
- str_value = Topology.JSONString(value)
224
- attr = topologic.StringAttribute(str_value) # Hook to Core
225
- elif isinstance(value, dict):
226
- str_value = dict_to_json(value)
227
- attr = topologic.StringAttribute(str_value) # Hook to Core
228
- elif isinstance(value, str):
229
- attr = topologic.StringAttribute(value) # Hook to Core
230
- elif isinstance(value, tuple):
231
- l = [Dictionary._ConvertValue(v) for v in list(value)]
232
- attr = topologic.ListAttribute(l) # Hook to Core
233
- elif isinstance(value, list):
234
- l = [Dictionary._ConvertValue(v) for v in value]
235
- attr = topologic.ListAttribute(l) # Hook to Core
236
- else:
237
- attr = topologic.StringAttribute("__NONE__") # Hook to Core
238
- return attr
239
-
240
455
  @staticmethod
241
456
  def ByKeysValues(keys, values, silent: bool = False):
242
457
  """
@@ -500,7 +715,71 @@ class Dictionary():
500
715
  keys = Dictionary.Keys(dictionary)
501
716
  values = Dictionary.Values(dictionary)
502
717
  return Dictionary.ByKeysValues(keys, values)
503
-
718
+
719
+ @staticmethod
720
+ def Difference(dictionaryA, dictionaryB, silent: bool = False):
721
+ """
722
+ Returns the difference of dictionaryA and dictionaryB (A \\ B), based on keys.
723
+
724
+ Procedure
725
+ ---------
726
+ 1. Validate inputs.
727
+ 2. If dictionaryA is None, return None.
728
+ 3. If dictionaryB is None, return dictionaryA.
729
+ 4. Return a new dictionary containing only keys found in dictionaryA but not
730
+ found in dictionaryB. Values are derived from dictionaryA.
731
+
732
+ Parameters
733
+ ----------
734
+ dictionaryA : topologic_core.Dictionary
735
+ The first input dictionary (minuend).
736
+ dictionaryB : topologic_core.Dictionary
737
+ The second input dictionary (subtrahend).
738
+ silent : bool , optional
739
+ If set to True, error and warning messages are suppressed. Default is False.
740
+
741
+ Returns
742
+ -------
743
+ topologic_core.Dictionary
744
+ The resulting difference dictionary.
745
+
746
+ """
747
+ from topologicpy.Topology import Topology
748
+
749
+ if dictionaryA is None:
750
+ if not silent:
751
+ print("Dictionary.Difference - Warning: The dictionaryA input parameter is None. Returning None.")
752
+ return None
753
+
754
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
755
+ if not silent:
756
+ print("Dictionary.Difference - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
757
+ return None
758
+
759
+ if dictionaryB is None:
760
+ if not silent:
761
+ print("Dictionary.Difference - Warning: The dictionaryB input parameter is None. Returning dictionaryA.")
762
+ return dictionaryA
763
+
764
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
765
+ if not silent:
766
+ print("Dictionary.Difference - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
767
+ return None
768
+
769
+ keysA = Dictionary.Keys(dictionaryA) or []
770
+ keysB = Dictionary.Keys(dictionaryB) or []
771
+
772
+ out_keys = [k for k in keysA if k not in keysB]
773
+
774
+ if len(out_keys) == 0:
775
+ if not silent:
776
+ print("Dictionary.Difference - Warning: There are no keys in dictionaryA that are not in dictionaryB. Returning and empty dictionary.")
777
+ return Dictionary.ByKeysValues([], [])
778
+
779
+ out_vals = [Dictionary.ValueAtKey(dictionaryA, k) for k in out_keys]
780
+
781
+ return Dictionary.ByKeysValues(out_keys, out_vals)
782
+
504
783
  @staticmethod
505
784
  def Filter(elements, dictionaries, searchType="any", key=None, value=None):
506
785
  """
@@ -592,22 +871,288 @@ class Dictionary():
592
871
  otherElements.append(elements[i])
593
872
  return {"filteredDictionaries": filteredDictionaries, "otherDictionaries": otherDictionaries, "filteredIndices": filteredIndices, "otherIndices": otherIndices, "filteredElements": filteredElements, "otherElements": otherElements}
594
873
 
874
+
595
875
  @staticmethod
596
- def Keys(dictionary, silent: bool = False):
876
+ def Intersection(dictionaryA, dictionaryB, silent: bool = False):
597
877
  """
598
- Returns the keys of the input dictionary.
599
-
878
+ Returns the intersection of dA and dB, based on keys, using a two-step
879
+ construction and Dictionary.ByMergedDictionaries.
880
+
881
+ Procedure
882
+ ---------
883
+ 1. Compute the common keys between dA and dB.
884
+ 2. Build two new dictionaries (dA_1 and dB_1) that contain exactly the
885
+ same common keys:
886
+ - dA_1 values are taken from dA
887
+ - dB_1 values are taken from dB
888
+ 3. Return Dictionary.ByMergedDictionaries(dA_1, dB_1)
889
+
600
890
  Parameters
601
891
  ----------
602
- dictionary : topologic_core.Dictionary or dict
603
- The input dictionary.
892
+ dictionaryA : topologic_core.Dictionary
893
+ The first input dictionary.
894
+ dictionaryB : topologic_core.Dictionary
895
+ The second input dictionary.
604
896
  silent : bool , optional
605
897
  If set to True, error and warning messages are suppressed. Default is False.
606
898
 
607
899
  Returns
608
900
  -------
609
- list
610
- The list of keys of the input dictionary.
901
+ topologic_core.Dictionary
902
+ The resulting intersection dictionary.
903
+
904
+ """
905
+ from topologicpy.Topology import Topology
906
+
907
+ if dictionaryA is None or dictionaryB is None:
908
+ if not silent:
909
+ print("Dictionary.Intersection - Warning: One or both of the input dictionaries is None. Returning and empty dictionary.")
910
+ return Dictionary.ByKeysValues([], [])
911
+
912
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
913
+ if not silent:
914
+ print("Dictionary.Intersection - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
915
+ return None
916
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
917
+ if not silent:
918
+ print("Dictionary.Intersection - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
919
+ return None
920
+
921
+ keysA = Dictionary.Keys(dictionaryA) or []
922
+ keysB = Dictionary.Keys(dictionaryB) or []
923
+
924
+ common_keys = [k for k in keysA if k in keysB]
925
+
926
+ if len(common_keys) == 0:
927
+ if not silent:
928
+ print("Dictionary.Intersection - Warning: There are no common keys in the input dictionaries. Returning and empty dictionary.")
929
+ return Dictionary.ByKeysValues([], [])
930
+
931
+ a_vals = [Dictionary.ValueAtKey(dictionaryA, k) for k in common_keys]
932
+ b_vals = [Dictionary.ValueAtKey(dictionaryB, k) for k in common_keys]
933
+
934
+ dA_1 = Dictionary.ByKeysValues(common_keys, a_vals)
935
+ dB_1 = Dictionary.ByKeysValues(common_keys, b_vals)
936
+
937
+ return Dictionary.ByMergedDictionaries(dA_1, dB_1)
938
+
939
+ @staticmethod
940
+ def Impose(dictionaryA, dictionaryB, silent: bool = False):
941
+ """
942
+ Imposes dictionaryB onto dictionaryA.
943
+
944
+ Procedure
945
+ ---------
946
+ 1. Validate inputs.
947
+ 2. If dictionaryA is None, return None.
948
+ 3. If dictionaryB is None, return dictionaryA.
949
+ 4. Create a new dictionary that:
950
+ - overwrites values in dictionaryA with values from dictionaryB for overlapping keys
951
+ - adds keys/values from dictionaryB that do not exist in dictionaryA
952
+
953
+ Parameters
954
+ ----------
955
+ dictionaryA : topologic_core.Dictionary
956
+ The first input dictionary (base).
957
+ dictionaryB : topologic_core.Dictionary
958
+ The second input dictionary (imposed).
959
+ silent : bool , optional
960
+ If set to True, error and warning messages are suppressed. Default is False.
961
+
962
+ Returns
963
+ -------
964
+ topologic_core.Dictionary
965
+ The resulting imposed dictionary.
966
+
967
+ """
968
+ from topologicpy.Topology import Topology
969
+
970
+ if dictionaryA is None:
971
+ if not silent:
972
+ print("Dictionary.Impose - Warning: The dictionaryA input parameter is None. Returning None.")
973
+ return None
974
+
975
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
976
+ if not silent:
977
+ print("Dictionary.Impose - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
978
+ return None
979
+
980
+ if dictionaryB is None:
981
+ if not silent:
982
+ print("Dictionary.Impose - Warning: The dictionaryB input parameter is None. Returning dictionaryA.")
983
+ return dictionaryA
984
+
985
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
986
+ if not silent:
987
+ print("Dictionary.Impose - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
988
+ return None
989
+
990
+ keysA = Dictionary.Keys(dictionaryA) or []
991
+ keysB = Dictionary.Keys(dictionaryB) or []
992
+
993
+ # Preserve A order first
994
+ out_keys = list(keysA)
995
+ out_vals = [Dictionary.ValueAtKey(dictionaryA, k) for k in keysA]
996
+ index_map = {k: i for i, k in enumerate(out_keys)}
997
+
998
+ for k in keysB:
999
+ vB = Dictionary.ValueAtKey(dictionaryB, k)
1000
+ if k in index_map:
1001
+ out_vals[index_map[k]] = vB
1002
+ else:
1003
+ index_map[k] = len(out_keys)
1004
+ out_keys.append(k)
1005
+ out_vals.append(vB)
1006
+
1007
+ return Dictionary.ByKeysValues(out_keys, out_vals)
1008
+
1009
+ @staticmethod
1010
+ def Imprint(dictionaryA, dictionaryB, silent: bool = False):
1011
+ """
1012
+ Imprints dictionaryB onto dictionaryA.
1013
+
1014
+ Procedure
1015
+ ---------
1016
+ 1. Validate inputs.
1017
+ 2. If dictionaryA is None, return None.
1018
+ 3. If dictionaryB is None, return dictionaryA.
1019
+ 4. Create a new dictionary that:
1020
+ - overwrites values in dictionaryA with values from dictionaryB for overlapping keys
1021
+ - ignores keys in dictionaryB that do not exist in dictionaryA
1022
+
1023
+ Parameters
1024
+ ----------
1025
+ dictionaryA : topologic_core.Dictionary
1026
+ The first input dictionary (base).
1027
+ dictionaryB : topologic_core.Dictionary
1028
+ The second input dictionary (imprinted).
1029
+ silent : bool , optional
1030
+ If set to True, error and warning messages are suppressed. Default is False.
1031
+
1032
+ Returns
1033
+ -------
1034
+ topologic_core.Dictionary
1035
+ The resulting imprinted dictionary.
1036
+
1037
+ """
1038
+ from topologicpy.Topology import Topology
1039
+
1040
+ if dictionaryA is None:
1041
+ if not silent:
1042
+ print("Dictionary.Imprint - Warning: The dictionaryA input parameter is None. Returning None.")
1043
+ return None
1044
+
1045
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1046
+ if not silent:
1047
+ print("Dictionary.Imprint - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1048
+ return None
1049
+
1050
+ if dictionaryB is None:
1051
+ if not silent:
1052
+ print("Dictionary.Imprint - Warning: The dictionaryB input parameter is None. Returning dictionaryA.")
1053
+ return dictionaryA
1054
+
1055
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1056
+ if not silent:
1057
+ print("Dictionary.Imprint - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1058
+ return None
1059
+
1060
+ keysA = Dictionary.Keys(dictionaryA) or []
1061
+ keysB = Dictionary.Keys(dictionaryB) or []
1062
+
1063
+ keysB_set = set(keysB)
1064
+
1065
+ out_keys = list(keysA)
1066
+ out_vals = []
1067
+ for k in keysA:
1068
+ if k in keysB_set:
1069
+ out_vals.append(Dictionary.ValueAtKey(dictionaryB, k))
1070
+ else:
1071
+ out_vals.append(Dictionary.ValueAtKey(dictionaryA, k))
1072
+
1073
+ return Dictionary.ByKeysValues(out_keys, out_vals)
1074
+
1075
+ @staticmethod
1076
+ def Intersection(dictionaryA, dictionaryB, silent: bool = False):
1077
+ """
1078
+ Returns the intersection of dA and dB, based on keys, using a two-step
1079
+ construction and Dictionary.ByMergedDictionaries.
1080
+
1081
+ Procedure
1082
+ ---------
1083
+ 1. Compute the common keys between dA and dB.
1084
+ 2. Build two new dictionaries (dA_1 and dB_1) that contain exactly the
1085
+ same common keys:
1086
+ - dA_1 values are taken from dA
1087
+ - dB_1 values are taken from dB
1088
+ 3. Return Dictionary.ByMergedDictionaries(dA_1, dB_1)
1089
+
1090
+ Parameters
1091
+ ----------
1092
+ dictionaryA : topologic_core.Dictionary
1093
+ The first input dictionary.
1094
+ dictionaryB : topologic_core.Dictionary
1095
+ The second input dictionary.
1096
+ silent : bool , optional
1097
+ If set to True, warnings and errors are suppressed. Default is False.
1098
+
1099
+ Returns
1100
+ -------
1101
+ topologic_core.Dictionary
1102
+ The resulting intersection dictionary.
1103
+
1104
+ """
1105
+
1106
+ from topologicpy.Topology import Topology
1107
+
1108
+ if dictionaryA is None or dictionaryB is None:
1109
+ if not silent:
1110
+ print("Dictionary.Intersection - Warning: One or both of the input dictionaries is None. Returning and empty dictionary.")
1111
+ return Dictionary.ByKeysValues([], [])
1112
+
1113
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1114
+ if not silent:
1115
+ print("Dictionary.Intersection - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1116
+ return None
1117
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1118
+ if not silent:
1119
+ print("Dictionary.Intersection - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1120
+ return None
1121
+
1122
+ keysA = Dictionary.Keys(dictionaryA) or []
1123
+ keysB = Dictionary.Keys(dictionaryB) or []
1124
+
1125
+ common_keys = [k for k in keysA if k in keysB]
1126
+
1127
+ if len(common_keys) == 0:
1128
+ if not silent:
1129
+ print("Dictionary.Intersection - Warning: There are no common keys in the input dictionaries. Returning and empty dictionary.")
1130
+ return Dictionary.ByKeysValues([], [])
1131
+
1132
+ a_vals = [Dictionary.ValueAtKey(dictionaryA, k) for k in common_keys]
1133
+ b_vals = [Dictionary.ValueAtKey(dictionaryB, k) for k in common_keys]
1134
+
1135
+ dA_1 = Dictionary.ByKeysValues(common_keys, a_vals)
1136
+ dB_1 = Dictionary.ByKeysValues(common_keys, b_vals)
1137
+
1138
+ return Dictionary.ByMergedDictionaries(dA_1, dB_1)
1139
+
1140
+ @staticmethod
1141
+ def Keys(dictionary, silent: bool = False):
1142
+ """
1143
+ Returns the keys of the input dictionary.
1144
+
1145
+ Parameters
1146
+ ----------
1147
+ dictionary : topologic_core.Dictionary or dict
1148
+ The input dictionary.
1149
+ silent : bool , optional
1150
+ If set to True, error and warning messages are suppressed. Default is False.
1151
+
1152
+ Returns
1153
+ -------
1154
+ list
1155
+ The list of keys of the input dictionary.
611
1156
 
612
1157
  """
613
1158
  from topologicpy.Topology import Topology
@@ -646,7 +1191,65 @@ class Dictionary():
646
1191
  listAttributes = listAttribute.ListValue()
647
1192
  returnList = [Dictionary._ConvertAttribute(attr) for attr in listAttributes]
648
1193
  return returnList
649
-
1194
+
1195
+ @staticmethod
1196
+ def Merge(dictionaryA, dictionaryB, silent: bool = False):
1197
+ """
1198
+ Returns the merge (union) of dictionaryA and dictionaryB.
1199
+
1200
+ Procedure
1201
+ ---------
1202
+ 1. Validate inputs.
1203
+ 2. If either input is None, return the other (if valid).
1204
+ 3. Return Dictionary.ByMergedDictionaries(dictionaryA, dictionaryB).
1205
+
1206
+ Parameters
1207
+ ----------
1208
+ dictionaryA : topologic_core.Dictionary
1209
+ The first input dictionary.
1210
+ dictionaryB : topologic_core.Dictionary
1211
+ The second input dictionary.
1212
+ silent : bool , optional
1213
+ If set to True, error and warning messages are suppressed. Default is False.
1214
+
1215
+ Returns
1216
+ -------
1217
+ topologic_core.Dictionary
1218
+ The resulting merged dictionary.
1219
+
1220
+ """
1221
+ from topologicpy.Topology import Topology
1222
+
1223
+ if dictionaryA is None and dictionaryB is None:
1224
+ if not silent:
1225
+ print("Dictionary.Merge - Warning: Both of the input dictionaries are None. Returning None.")
1226
+ return None
1227
+
1228
+ if dictionaryA is None:
1229
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1230
+ if not silent:
1231
+ print("Dictionary.Merge - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1232
+ return None
1233
+ return dictionaryB
1234
+
1235
+ if dictionaryB is None:
1236
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1237
+ if not silent:
1238
+ print("Dictionary.Merge - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1239
+ return None
1240
+ return dictionaryA
1241
+
1242
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1243
+ if not silent:
1244
+ print("Dictionary.Merge - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1245
+ return None
1246
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1247
+ if not silent:
1248
+ print("Dictionary.Merge - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1249
+ return None
1250
+
1251
+ return Dictionary.ByMergedDictionaries(dictionaryA, dictionaryB)
1252
+
650
1253
  @staticmethod
651
1254
  def PythonDictionary(dictionary, silent: bool = False):
652
1255
  """
@@ -854,92 +1457,226 @@ class Dictionary():
854
1457
  return dictionary
855
1458
 
856
1459
  @staticmethod
857
- def _ConvertAttribute(attr):
1460
+ def SymDif(dictionaryA, dictionaryB, silent: bool = False):
858
1461
  """
859
- Convert the found attribute into the proper value
1462
+ Returns the symmetric difference (XOR) of dictionaryA and dictionaryB, based on keys.
1463
+
1464
+ Procedure
1465
+ ---------
1466
+ 1. Validate inputs.
1467
+ 2. Compute keys that exist in exactly one of the two dictionaries.
1468
+ 3. Create a new dictionary containing:
1469
+ - unique keys from dictionaryA with values from dictionaryA
1470
+ - unique keys from dictionaryB with values from dictionaryB
1471
+
1472
+ Parameters
1473
+ ----------
1474
+ dictionaryA : topologic_core.Dictionary
1475
+ The first input dictionary.
1476
+ dictionaryB : topologic_core.Dictionary
1477
+ The second input dictionary.
1478
+ silent : bool , optional
1479
+ If set to True, error and warning messages are suppressed. Default is False.
1480
+
1481
+ Returns
1482
+ -------
1483
+ topologic_core.Dictionary
1484
+ The resulting symmetric difference dictionary.
1485
+
860
1486
  """
861
1487
  from topologicpy.Topology import Topology
862
- import json
863
1488
 
864
- def is_json_string(input_string):
865
- """
866
- Check if the input string is a valid JSON string.
867
- """
868
- try:
869
- json.loads(input_string)
870
- return True
871
- except json.JSONDecodeError:
872
- return False
1489
+ if dictionaryA is None and dictionaryB is None:
1490
+ if not silent:
1491
+ print("Dictionary.SymDif - Warning: Both of the input dictionaries are None. Returning and empty dictionary.")
1492
+ return Dictionary.ByKeysValues([], [])
873
1493
 
874
- def json_to_dict(json_string):
875
- """
876
- Convert a JSON-formatted string to a Python dictionary.
877
- """
878
- return json.loads(json_string)
1494
+ if dictionaryA is None:
1495
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1496
+ if not silent:
1497
+ print("Dictionary.SymDif - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1498
+ return None
1499
+ return dictionaryB
1500
+
1501
+ if dictionaryB is None:
1502
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1503
+ if not silent:
1504
+ print("Dictionary.SymDif - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1505
+ return None
1506
+ return dictionaryA
1507
+
1508
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1509
+ if not silent:
1510
+ print("Dictionary.SymDif - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1511
+ return None
1512
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1513
+ if not silent:
1514
+ print("Dictionary.SymDif - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1515
+ return None
1516
+
1517
+ keysA = Dictionary.Keys(dictionaryA) or []
1518
+ keysB = Dictionary.Keys(dictionaryB) or []
1519
+
1520
+ uniqueA = [k for k in keysA if k not in keysB]
1521
+ uniqueB = [k for k in keysB if k not in keysA]
1522
+
1523
+ if len(uniqueA) == 0 and len(uniqueB) == 0:
1524
+ if not silent:
1525
+ print("Dictionary.SymDif - Warning: The input dictionaries have identical keys. Returning and empty dictionary.")
1526
+ return Dictionary.ByKeysValues([], [])
1527
+
1528
+ out_keys = []
1529
+ out_vals = []
1530
+
1531
+ for k in uniqueA:
1532
+ out_keys.append(k)
1533
+ out_vals.append(Dictionary.ValueAtKey(dictionaryA, k))
1534
+
1535
+ for k in uniqueB:
1536
+ out_keys.append(k)
1537
+ out_vals.append(Dictionary.ValueAtKey(dictionaryB, k))
1538
+
1539
+ return Dictionary.ByKeysValues(out_keys, out_vals)
879
1540
 
880
- if isinstance(attr, IntAttribute):
881
- return (attr.IntValue())
882
- elif isinstance(attr, DoubleAttribute):
883
- return (attr.DoubleValue())
884
- elif isinstance(attr, StringAttribute):
885
- temp_value = attr.StringValue()
886
- topologies = None
887
- try:
888
- if is_json_string(temp_value):
889
- topologies = Topology.ByJSONString(temp_value)
890
- else:
891
- topologies = None
892
- except:
893
- topologies = None
894
- if isinstance(topologies, list):
895
- if len(topologies) == 0:
896
- topologies = None
897
- if temp_value == "__NONE__":
1541
+ @staticmethod
1542
+ def SymmetricDifference(dictionaryA, dictionaryB, silent: bool = False):
1543
+ """
1544
+ Returns the symmetric difference (XOR) of dictionaryA and dictionaryB, based on keys.
1545
+
1546
+ Procedure
1547
+ ---------
1548
+ 1. Validate inputs.
1549
+ 2. Compute keys that exist in exactly one of the two dictionaries.
1550
+ 3. Create a new dictionary containing:
1551
+ - unique keys from dictionaryA with values from dictionaryA
1552
+ - unique keys from dictionaryB with values from dictionaryB
1553
+
1554
+ Parameters
1555
+ ----------
1556
+ dictionaryA : topologic_core.Dictionary
1557
+ The first input dictionary.
1558
+ dictionaryB : topologic_core.Dictionary
1559
+ The second input dictionary.
1560
+ silent : bool , optional
1561
+ If set to True, error and warning messages are suppressed. Default is False.
1562
+
1563
+ Returns
1564
+ -------
1565
+ topologic_core.Dictionary
1566
+ The resulting symmetric difference dictionary.
1567
+
1568
+ """
1569
+ from topologicpy.Topology import Topology
1570
+
1571
+ if dictionaryA is None and dictionaryB is None:
1572
+ if not silent:
1573
+ print("Dictionary.SymmetricDifference - Warning: Both of the input dictionaries are None. Returning and empty dictionary.")
1574
+ return Dictionary.ByKeysValues([], [])
1575
+
1576
+ if dictionaryA is None:
1577
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1578
+ if not silent:
1579
+ print("Dictionary.SymmetricDifference - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
898
1580
  return None
899
- elif Topology.IsInstance(topologies, "Topology"):
900
- return topologies
901
- elif isinstance(topologies, list):
902
- if len(topologies) > 1:
903
- return topologies
904
- elif len(topologies) == 1:
905
- return topologies[0]
906
- elif is_json_string(temp_value):
907
- ret_value = json_to_dict(temp_value)
908
- return ret_value
909
- else:
910
- return (temp_value)
911
- elif isinstance(attr, ListAttribute):
912
- return (Dictionary.ListAttributeValues(attr))
913
- elif isinstance(attr, float) or isinstance(attr, int):
914
- return attr
915
- elif isinstance(attr, str):
916
- if attr == "__NONE__":
1581
+ return dictionaryB
1582
+
1583
+ if dictionaryB is None:
1584
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1585
+ if not silent:
1586
+ print("Dictionary.SymmetricDifference - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
917
1587
  return None
918
- topologies = None
919
- try:
920
- if is_json_string(attr):
921
- topologies = Topology.ByJSONString(attr)
922
- else:
923
- topologies = None
924
- except:
925
- topologies = None
926
- if isinstance(topologies, list):
927
- if len(topologies) > 1:
928
- return topologies
929
- elif len(topologies) == 1:
930
- return topologies[0]
931
- elif is_json_string(attr):
932
- return json_to_dict(attr)
933
- else:
934
- return (attr)
935
- elif isinstance(attr, tuple):
936
- return Dictionary.ListAttributeValues([Dictionary._ConvertAttribute(x) for x in list(attr)])
937
- elif isinstance(attr, list):
938
- return Dictionary.ListAttributeValues([Dictionary._ConvertAttribute(x) for x in attr])
939
- elif isinstance(attr, dict):
940
- return attr
941
- else:
1588
+ return dictionaryA
1589
+
1590
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1591
+ if not silent:
1592
+ print("Dictionary.SymmetricDifference - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1593
+ return None
1594
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1595
+ if not silent:
1596
+ print("Dictionary.SymmetricDifference - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1597
+ return None
1598
+
1599
+ keysA = Dictionary.Keys(dictionaryA) or []
1600
+ keysB = Dictionary.Keys(dictionaryB) or []
1601
+
1602
+ uniqueA = [k for k in keysA if k not in keysB]
1603
+ uniqueB = [k for k in keysB if k not in keysA]
1604
+
1605
+ if len(uniqueA) == 0 and len(uniqueB) == 0:
1606
+ if not silent:
1607
+ print("Dictionary.SymmetricDifference - Warning: The input dictionaries have identical keys. Returning and empty dictionary.")
1608
+ return Dictionary.ByKeysValues([], [])
1609
+
1610
+ out_keys = []
1611
+ out_vals = []
1612
+
1613
+ for k in uniqueA:
1614
+ out_keys.append(k)
1615
+ out_vals.append(Dictionary.ValueAtKey(dictionaryA, k))
1616
+
1617
+ for k in uniqueB:
1618
+ out_keys.append(k)
1619
+ out_vals.append(Dictionary.ValueAtKey(dictionaryB, k))
1620
+
1621
+ return Dictionary.ByKeysValues(out_keys, out_vals)
1622
+
1623
+ @staticmethod
1624
+ def Union(dictionaryA, dictionaryB, silent: bool = False):
1625
+ """
1626
+ Returns the union (merge) of dictionaryA and dictionaryB.
1627
+
1628
+ Procedure
1629
+ ---------
1630
+ 1. Validate inputs.
1631
+ 2. If either input is None, return the other (if valid).
1632
+ 3. Return Dictionary.ByMergedDictionaries(dictionaryA, dictionaryB).
1633
+
1634
+ Parameters
1635
+ ----------
1636
+ dictionaryA : topologic_core.Dictionary
1637
+ The first input dictionary.
1638
+ dictionaryB : topologic_core.Dictionary
1639
+ The second input dictionary.
1640
+ silent : bool , optional
1641
+ If set to True, error and warning messages are suppressed. Default is False.
1642
+
1643
+ Returns
1644
+ -------
1645
+ topologic_core.Dictionary
1646
+ The resulting union dictionary.
1647
+
1648
+ """
1649
+ from topologicpy.Topology import Topology
1650
+
1651
+ if dictionaryA is None and dictionaryB is None:
1652
+ if not silent:
1653
+ print("Dictionary.Union - Warning: Both of the input dictionaries are None. Returning None.")
1654
+ return None
1655
+
1656
+ if dictionaryA is None:
1657
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1658
+ if not silent:
1659
+ print("Dictionary.Union - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1660
+ return None
1661
+ return dictionaryB
1662
+
1663
+ if dictionaryB is None:
1664
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1665
+ if not silent:
1666
+ print("Dictionary.Union - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1667
+ return None
1668
+ return dictionaryA
1669
+
1670
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1671
+ if not silent:
1672
+ print("Dictionary.Union - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1673
+ return None
1674
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1675
+ if not silent:
1676
+ print("Dictionary.Union - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
942
1677
  return None
1678
+
1679
+ return Dictionary.ByMergedDictionaries(dictionaryA, dictionaryB)
943
1680
 
944
1681
  @staticmethod
945
1682
  def ValueAtKey(dictionary, key, defaultValue=None, silent=False):
@@ -1023,7 +1760,7 @@ class Dictionary():
1023
1760
  return None
1024
1761
  returnList.append(attr)
1025
1762
  return returnList
1026
-
1763
+
1027
1764
  @staticmethod
1028
1765
  def ValuesAtKeys(dictionary, keys, defaultValue=None, silent: bool = False):
1029
1766
  """
@@ -1067,5 +1804,86 @@ class Dictionary():
1067
1804
  print("Dictionary.ValuesAtKeys - Error: The input keys parameter contains invalid values. Returning None.")
1068
1805
  return None
1069
1806
  return [Dictionary.ValueAtKey(dictionary, key, defaultValue=defaultValue, silent=silent) for key in keys]
1070
-
1807
+
1808
+ @staticmethod
1809
+ def XOR(dictionaryA, dictionaryB, silent: bool = False):
1810
+ """
1811
+ Returns the XOR (symmetric difference) of dictionaryA and dictionaryB, based on keys.
1812
+
1813
+ Procedure
1814
+ ---------
1815
+ 1. Validate inputs.
1816
+ 2. Compute keys that exist in exactly one of the two dictionaries.
1817
+ 3. Create a new dictionary containing:
1818
+ - unique keys from dictionaryA with values from dictionaryA
1819
+ - unique keys from dictionaryB with values from dictionaryB
1820
+
1821
+ Parameters
1822
+ ----------
1823
+ dictionaryA : topologic_core.Dictionary
1824
+ The first input dictionary.
1825
+ dictionaryB : topologic_core.Dictionary
1826
+ The second input dictionary.
1827
+ silent : bool , optional
1828
+ If set to True, error and warning messages are suppressed. Default is False.
1829
+
1830
+ Returns
1831
+ -------
1832
+ topologic_core.Dictionary
1833
+ The resulting XOR (symmetric difference) dictionary.
1834
+
1835
+ """
1836
+ from topologicpy.Topology import Topology
1837
+
1838
+ if dictionaryA is None and dictionaryB is None:
1839
+ if not silent:
1840
+ print("Dictionary.XOR - Warning: Both of the input dictionaries are None. Returning and empty dictionary.")
1841
+ return Dictionary.ByKeysValues([], [])
1842
+
1843
+ if dictionaryA is None:
1844
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1845
+ if not silent:
1846
+ print("Dictionary.XOR - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1847
+ return None
1848
+ return dictionaryB
1849
+
1850
+ if dictionaryB is None:
1851
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1852
+ if not silent:
1853
+ print("Dictionary.XOR - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1854
+ return None
1855
+ return dictionaryA
1856
+
1857
+ if not Topology.IsInstance(dictionaryA, "Dictionary"):
1858
+ if not silent:
1859
+ print("Dictionary.XOR - Error: the dictionaryA input parameter is not a valid dictionary. Returning None.")
1860
+ return None
1861
+ if not Topology.IsInstance(dictionaryB, "Dictionary"):
1862
+ if not silent:
1863
+ print("Dictionary.XOR - Error: the dictionaryB input parameter is not a valid dictionary. Returning None.")
1864
+ return None
1865
+
1866
+ keysA = Dictionary.Keys(dictionaryA) or []
1867
+ keysB = Dictionary.Keys(dictionaryB) or []
1868
+
1869
+ uniqueA = [k for k in keysA if k not in keysB]
1870
+ uniqueB = [k for k in keysB if k not in keysA]
1871
+
1872
+ if len(uniqueA) == 0 and len(uniqueB) == 0:
1873
+ if not silent:
1874
+ print("Dictionary.XOR - Warning: The input dictionaries have identical keys. Returning and empty dictionary.")
1875
+ return Dictionary.ByKeysValues([], [])
1876
+
1877
+ out_keys = []
1878
+ out_vals = []
1879
+
1880
+ for k in uniqueA:
1881
+ out_keys.append(k)
1882
+ out_vals.append(Dictionary.ValueAtKey(dictionaryA, k))
1883
+
1884
+ for k in uniqueB:
1885
+ out_keys.append(k)
1886
+ out_vals.append(Dictionary.ValueAtKey(dictionaryB, k))
1887
+
1888
+ return Dictionary.ByKeysValues(out_keys, out_vals)
1071
1889