fmdtools 2.5.2__tar.gz → 2.5.4__tar.gz

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.
Files changed (243) hide show
  1. {fmdtools-2.5.2 → fmdtools-2.5.4}/LICENSE +1 -1
  2. {fmdtools-2.5.2/fmdtools.egg-info → fmdtools-2.5.4}/PKG-INFO +5 -5
  3. {fmdtools-2.5.2 → fmdtools-2.5.4}/README.md +3 -3
  4. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/state.py +35 -28
  5. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/contingency_management/model_aircraft.py +2 -2
  6. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/contingency_management/model_environment.py +1 -1
  7. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/multirotor_drone/model_urban.py +4 -0
  8. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/water_pump/test_pump_stochastic.py +2 -2
  9. {fmdtools-2.5.2 → fmdtools-2.5.4/fmdtools.egg-info}/PKG-INFO +5 -5
  10. {fmdtools-2.5.2 → fmdtools-2.5.4}/fmdtools.egg-info/SOURCES.txt +82 -1
  11. {fmdtools-2.5.2 → fmdtools-2.5.4}/pyproject.toml +1 -1
  12. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/__init__.py +1 -1
  13. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/common.py +47 -22
  14. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/graph/base.py +12 -2
  15. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/history.py +59 -24
  16. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/phases.py +20 -9
  17. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/result.py +73 -28
  18. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/tabulate.py +26 -17
  19. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/architecture/action.py +1 -1
  20. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/architecture/geom.py +4 -1
  21. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/base.py +2 -1
  22. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/block/base.py +36 -16
  23. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/container/base.py +2 -2
  24. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/container/parameter.py +3 -2
  25. fmdtools-2.5.4/src/fmdtools/define/container/rand.py +956 -0
  26. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/container/state.py +15 -10
  27. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/container/time.py +9 -3
  28. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/object/coords.py +8 -3
  29. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/object/timer.py +9 -6
  30. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/sim/propagate.py +2 -1
  31. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/sim/sample.py +83 -27
  32. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/sim/scenario.py +33 -17
  33. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/sim/search.py +12 -7
  34. fmdtools-2.5.4/tests/test_appended_history_snapshots.py +172 -0
  35. fmdtools-2.5.4/tests/test_approach_joint_domains.py +181 -0
  36. fmdtools-2.5.4/tests/test_approach_model_timestep.py +192 -0
  37. fmdtools-2.5.4/tests/test_approach_phase_map_ownership.py +129 -0
  38. fmdtools-2.5.4/tests/test_array_differences.py +153 -0
  39. fmdtools-2.5.4/tests/test_array_result_equality.py +167 -0
  40. fmdtools-2.5.4/tests/test_array_state_limits.py +126 -0
  41. fmdtools-2.5.4/tests/test_bootstrap_statistic_arguments.py +196 -0
  42. fmdtools-2.5.4/tests/test_bounded_array_increments.py +141 -0
  43. fmdtools-2.5.4/tests/test_callable_parameter_domains.py +142 -0
  44. fmdtools-2.5.4/tests/test_choice_draw_mass.py +198 -0
  45. fmdtools-2.5.4/tests/test_choice_probability_precision.py +173 -0
  46. fmdtools-2.5.4/tests/test_common.py +166 -0
  47. fmdtools-2.5.4/tests/test_coordinate_cell_boundaries.py +124 -0
  48. fmdtools-2.5.4/tests/test_default_fault_sample_timestep.py +162 -0
  49. fmdtools-2.5.4/tests/test_degradation_path_boundaries.py +153 -0
  50. fmdtools-2.5.4/tests/test_dirichlet_batches.py +195 -0
  51. fmdtools-2.5.4/tests/test_disconnected_graph_robustness.py +154 -0
  52. fmdtools-2.5.4/tests/test_discrete_sample_sizes.py +252 -0
  53. fmdtools-2.5.4/tests/test_exact_nominal_expectation.py +166 -0
  54. fmdtools-2.5.4/tests/test_explicit_result_file_formats.py +176 -0
  55. fmdtools-2.5.4/tests/test_exponential_rayleigh_draw_sizes.py +239 -0
  56. fmdtools-2.5.4/tests/test_factor_sort_priority.py +142 -0
  57. fmdtools-2.5.4/tests/test_fault_history_paths.py +179 -0
  58. fmdtools-2.5.4/tests/test_fault_weight_arrays.py +150 -0
  59. fmdtools-2.5.4/tests/test_faulty_scenario_selection.py +143 -0
  60. fmdtools-2.5.4/tests/test_fmea_input_ownership.py +126 -0
  61. fmdtools-2.5.4/tests/test_gamma_sampling_density.py +233 -0
  62. fmdtools-2.5.4/tests/test_generated_history_phase_timestep.py +92 -0
  63. fmdtools-2.5.4/tests/test_graph_edge_comparison.py +129 -0
  64. fmdtools-2.5.4/tests/test_history_phase_identifiers.py +204 -0
  65. fmdtools-2.5.4/tests/test_independent_single_replicate_seeds.py +201 -0
  66. fmdtools-2.5.4/tests/test_individual_result_paths.py +165 -0
  67. fmdtools-2.5.4/tests/test_integer_draw_mass.py +266 -0
  68. fmdtools-2.5.4/tests/test_join_phase_timestep.py +118 -0
  69. fmdtools-2.5.4/tests/test_joint_fault_metadata_order.py +137 -0
  70. fmdtools-2.5.4/tests/test_joint_fault_phase_map.py +192 -0
  71. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_joint_fault_weights.py +2 -1
  72. fmdtools-2.5.4/tests/test_json_result_arrays.py +163 -0
  73. fmdtools-2.5.4/tests/test_location_scale_draw_sizes.py +268 -0
  74. fmdtools-2.5.4/tests/test_lognormal_defaults.py +180 -0
  75. fmdtools-2.5.4/tests/test_metric_weight_alignment.py +140 -0
  76. fmdtools-2.5.4/tests/test_mixed_shape_degradation.py +180 -0
  77. fmdtools-2.5.4/tests/test_multidimensional_state_comparison.py +96 -0
  78. fmdtools-2.5.4/tests/test_multinomial_sampling_arguments.py +195 -0
  79. fmdtools-2.5.4/tests/test_multivariate_hypergeometric_options.py +201 -0
  80. fmdtools-2.5.4/tests/test_multivariate_normal_arguments.py +199 -0
  81. fmdtools-2.5.4/tests/test_named_default_restoration.py +143 -0
  82. fmdtools-2.5.4/tests/test_named_metric_rounding.py +152 -0
  83. fmdtools-2.5.4/tests/test_nearest_point_exclusion.py +89 -0
  84. fmdtools-2.5.4/tests/test_nested_history_summary.py +120 -0
  85. fmdtools-2.5.4/tests/test_nested_parameter_constraints.py +148 -0
  86. fmdtools-2.5.4/tests/test_nested_scenario_parameters.py +167 -0
  87. fmdtools-2.5.4/tests/test_not_equal_constraints.py +138 -0
  88. fmdtools-2.5.4/tests/test_object_history_copy.py +140 -0
  89. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_packages.py +3 -3
  90. fmdtools-2.5.4/tests/test_parameter_grid_bounds.py +140 -0
  91. fmdtools-2.5.4/tests/test_parameter_sample_defaults.py +146 -0
  92. fmdtools-2.5.4/tests/test_pareto_density.py +203 -0
  93. fmdtools-2.5.4/tests/test_permutation_axis_probabilities.py +188 -0
  94. fmdtools-2.5.4/tests/test_permuted_slice_probabilities.py +184 -0
  95. fmdtools-2.5.4/tests/test_phase_map_default_isolation.py +91 -0
  96. fmdtools-2.5.4/tests/test_ragged_confidence_histories.py +166 -0
  97. fmdtools-2.5.4/tests/test_rand.py +177 -0
  98. fmdtools-2.5.4/tests/test_random_combination_types.py +169 -0
  99. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_result.py +107 -11
  100. fmdtools-2.5.4/tests/test_result_constructor_input.py +121 -0
  101. fmdtools-2.5.4/tests/test_result_folder_selection.py +191 -0
  102. fmdtools-2.5.4/tests/test_sample.py +501 -0
  103. fmdtools-2.5.4/tests/test_scenario.py +211 -0
  104. fmdtools-2.5.4/tests/test_shape_distribution_sizes.py +221 -0
  105. fmdtools-2.5.4/tests/test_simple_problem_vector_inputs.py +164 -0
  106. fmdtools-2.5.4/tests/test_simulation_exposure.py +160 -0
  107. fmdtools-2.5.4/tests/test_standard_draw_arguments.py +205 -0
  108. fmdtools-2.5.4/tests/test_standard_t_density.py +192 -0
  109. fmdtools-2.5.4/tests/test_string_state_histories.py +96 -0
  110. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_time.py +4 -4
  111. fmdtools-2.5.4/tests/test_time_reset.py +103 -0
  112. fmdtools-2.5.4/tests/test_triangular_arraylike_parameters.py +181 -0
  113. fmdtools-2.5.4/tests/test_tuple_table_factors.py +143 -0
  114. fmdtools-2.5.4/tests/test_unit_random_density.py +176 -0
  115. fmdtools-2.5.4/tests/test_vonmises_density.py +222 -0
  116. fmdtools-2.5.4/tests/test_wald_density.py +188 -0
  117. fmdtools-2.5.4/tests/test_weighted_percent_metrics.py +106 -0
  118. fmdtools-2.5.4/tests/test_zero_fault_start_times.py +120 -0
  119. fmdtools-2.5.4/tests/test_zero_time_objectives.py +117 -0
  120. fmdtools-2.5.4/tests/test_zero_timer_increment.py +130 -0
  121. fmdtools-2.5.2/src/fmdtools/define/container/rand.py +0 -668
  122. fmdtools-2.5.2/tests/test_sample.py +0 -165
  123. fmdtools-2.5.2/tests/test_scenario.py +0 -111
  124. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/__init__.py +0 -0
  125. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airport_taxiway/model_asset.py +0 -0
  126. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airport_taxiway/model_atc.py +0 -0
  127. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airport_taxiway/model_common.py +0 -0
  128. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airport_taxiway/model_main.py +0 -0
  129. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airport_taxiway/test_asset.py +0 -0
  130. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airport_taxiway/test_model.py +0 -0
  131. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/__init__.py +0 -0
  132. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/__init__.py +0 -0
  133. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/aircraft.py +0 -0
  134. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/aircraftarchitecture.py +0 -0
  135. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/arch/__init__.py +0 -0
  136. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/arch/aviate.py +0 -0
  137. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/arch/controlflight.py +0 -0
  138. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/arch/flows.py +0 -0
  139. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/arch/holdpayload.py +0 -0
  140. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/arch/perceiveenvironment.py +0 -0
  141. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/arch/storeee.py +0 -0
  142. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/base/parameter.py +0 -0
  143. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/contingency_management/__init__.py +0 -0
  144. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/contingency_management/model_flightplanner.py +0 -0
  145. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/water_rescue/__init__.py +0 -0
  146. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/water_rescue/model_drone.py +0 -0
  147. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/water_rescue/model_environment.py +0 -0
  148. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/water_rescue/model_main.py +0 -0
  149. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/water_rescue/model_responder.py +0 -0
  150. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/wildfire_response/__init__.py +0 -0
  151. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/wildfire_response/model_aircraft.py +0 -0
  152. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/wildfire_response/model_environment.py +0 -0
  153. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/airspacelib/wildfire_response/model_main.py +0 -0
  154. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/cooling_tank/__init__.py +0 -0
  155. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/cooling_tank/model_main.py +0 -0
  156. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/cooling_tank/model_optimization.py +0 -0
  157. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/cooling_tank/test_tank.py +0 -0
  158. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/electric_power_system/__init__.py +0 -0
  159. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/electric_power_system/model_main.py +0 -0
  160. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/electric_power_system/test_model.py +0 -0
  161. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/human_hazard_mitigation/__init__.py +0 -0
  162. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/human_hazard_mitigation/model_main.py +0 -0
  163. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/multirotor_drone/__init__.py +0 -0
  164. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/multirotor_drone/model_dynamic.py +0 -0
  165. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/multirotor_drone/model_hierarchical.py +0 -0
  166. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/multirotor_drone/model_rural.py +0 -0
  167. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/multirotor_drone/model_static.py +0 -0
  168. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/multirotor_drone/script_opt_model_rural.py +0 -0
  169. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/multirotor_drone/test_multirotor.py +0 -0
  170. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/navigating_rover/__init__.py +0 -0
  171. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/navigating_rover/model_degradation.py +0 -0
  172. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/navigating_rover/model_human.py +0 -0
  173. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/navigating_rover/model_main.py +0 -0
  174. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/navigating_rover/model_troupe.py +0 -0
  175. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/navigating_rover/script_ea_analysis.py +0 -0
  176. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/navigating_rover/script_mode_space.py +0 -0
  177. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/navigating_rover/script_search_rover.py +0 -0
  178. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/navigating_rover/test_rover.py +0 -0
  179. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/state_communication/__init__.py +0 -0
  180. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/state_communication/model_main.py +0 -0
  181. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/state_communication/test_multiflow.py +0 -0
  182. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/water_pump/__init__.py +0 -0
  183. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/water_pump/model_indiv.py +0 -0
  184. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/water_pump/model_main.py +0 -0
  185. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/water_pump/model_stochastic.py +0 -0
  186. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/water_pump/script_jointfaults.py +0 -0
  187. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/water_pump/script_parallelism.py +0 -0
  188. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/water_pump/test_pump.py +0 -0
  189. {fmdtools-2.5.2 → fmdtools-2.5.4}/examples/water_pump/test_pump_example.py +0 -0
  190. {fmdtools-2.5.2 → fmdtools-2.5.4}/fmdtools.egg-info/dependency_links.txt +0 -0
  191. {fmdtools-2.5.2 → fmdtools-2.5.4}/fmdtools.egg-info/requires.txt +0 -0
  192. {fmdtools-2.5.2 → fmdtools-2.5.4}/fmdtools.egg-info/top_level.txt +0 -0
  193. {fmdtools-2.5.2 → fmdtools-2.5.4}/setup.cfg +0 -0
  194. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/__init__.py +0 -0
  195. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/graph/__init__.py +0 -0
  196. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/graph/label.py +0 -0
  197. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/graph/model.py +0 -0
  198. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/analyze/graph/style.py +0 -0
  199. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/__init__.py +0 -0
  200. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/architecture/__init__.py +0 -0
  201. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/architecture/base.py +0 -0
  202. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/architecture/component.py +0 -0
  203. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/architecture/function.py +0 -0
  204. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/block/__init__.py +0 -0
  205. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/block/action.py +0 -0
  206. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/block/component.py +0 -0
  207. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/block/function.py +0 -0
  208. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/container/__init__.py +0 -0
  209. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/container/mode.py +0 -0
  210. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/environment.py +0 -0
  211. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/flow/__init__.py +0 -0
  212. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/flow/base.py +0 -0
  213. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/flow/commsflow.py +0 -0
  214. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/flow/multiflow.py +0 -0
  215. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/object/__init__.py +0 -0
  216. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/object/base.py +0 -0
  217. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/object/geom.py +0 -0
  218. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/define/pathplan.py +0 -0
  219. {fmdtools-2.5.2 → fmdtools-2.5.4}/src/fmdtools/sim/__init__.py +0 -0
  220. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/__init__.py +0 -0
  221. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/common.py +0 -0
  222. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_array_random_states.py +0 -0
  223. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_calc_metric_ci_constant_outputs.py +0 -0
  224. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_comparison_groups.py +0 -0
  225. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_confidence_history_clipping.py +0 -0
  226. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_error_history_plots.py +0 -0
  227. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_fault_sample_without_nominal.py +0 -0
  228. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_fault_time_without_traces.py +0 -0
  229. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_geom.py +0 -0
  230. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_graph.py +0 -0
  231. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_hierarchical.py +0 -0
  232. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_history_standard_deviation.py +0 -0
  233. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_join_phasemaps.py +0 -0
  234. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_nested_nominal_groups.py +0 -0
  235. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_nesting_custom_separators.py +0 -0
  236. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_parameter_sample_seeds.py +0 -0
  237. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_parameter_subsampling_types.py +0 -0
  238. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_pathplan.py +0 -0
  239. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_phase_timestep.py +0 -0
  240. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_pruned_fault_times.py +0 -0
  241. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_scenario_defaults.py +0 -0
  242. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_table_factor_indices.py +0 -0
  243. {fmdtools-2.5.2 → fmdtools-2.5.4}/tests/test_uniform_density.py +0 -0
@@ -1,4 +1,4 @@
1
- fmdtools version 2.5.2
1
+ fmdtools version 2.5.4
2
2
  ---------------------------
3
3
  Apache License
4
4
  Version 2.0, January 2004
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmdtools
3
- Version: 2.5.2
3
+ Version: 2.5.4
4
4
  Summary: System resilience modelling, simulation, and assessment in Python
5
5
  Author: fmdtools Developers
6
6
  Maintainer-email: Daniel Hulse <daniel.e.hulse@nasa.gov>
7
- License: fmdtools version 2.5.2
7
+ License: fmdtools version 2.5.4
8
8
  ---------------------------
9
9
  Apache License
10
10
  Version 2.0, January 2004
@@ -394,7 +394,7 @@ More guidance on development installations is provided in [How to Contribute](ht
394
394
  Note that this version reflects the latest version on the ``main`` git branch along with any local changes. To use a particular version of ``fmdtools`` in the context of a development install you can checkout the tag corresponding to the version you want, e.g.:
395
395
 
396
396
  ```
397
- git checkout v2.5.2
397
+ git checkout v2.5.4
398
398
  ```
399
399
 
400
400
 
@@ -548,8 +548,8 @@ To cite a particular version of the fmdtools, you may use:
548
548
  author = {{NASA}},
549
549
  title = {fmdtools},
550
550
  url = {https://github.com/nasa/fmdtools},
551
- version = {2.5.2},
552
- date = {2026-9-28},
551
+ version = {2.5.4},
552
+ date = {2026-10-5},
553
553
  }
554
554
  ```
555
555
 
@@ -123,7 +123,7 @@ More guidance on development installations is provided in [How to Contribute](ht
123
123
  Note that this version reflects the latest version on the ``main`` git branch along with any local changes. To use a particular version of ``fmdtools`` in the context of a development install you can checkout the tag corresponding to the version you want, e.g.:
124
124
 
125
125
  ```
126
- git checkout v2.5.2
126
+ git checkout v2.5.4
127
127
  ```
128
128
 
129
129
 
@@ -277,8 +277,8 @@ To cite a particular version of the fmdtools, you may use:
277
277
  author = {{NASA}},
278
278
  title = {fmdtools},
279
279
  url = {https://github.com/nasa/fmdtools},
280
- version = {2.5.2},
281
- date = {2026-9-28},
280
+ version = {2.5.4},
281
+ date = {2026-10-5},
282
282
  }
283
283
  ```
284
284
 
@@ -41,19 +41,26 @@ class AircraftPosition(State):
41
41
  dy: float = 0.0
42
42
  dims = ('x', 'y')
43
43
 
44
- def get_goal(self):
44
+ def get_dims(self, *dims):
45
+ """Get the dimensions to calculate over. Default is self.dims."""
46
+ if not dims:
47
+ return self.dims
48
+ else:
49
+ return dims
50
+
51
+ def get_goal(self, *dims):
45
52
  """Get the goal attributes (goal_x, goal_y) of the AircraftPosition."""
46
- return self.get(*['goal_'+d for d in self.dims])
53
+ return self.get(*['goal_'+d for d in self.get_dims(*dims)])
47
54
 
48
- def get_loc(self):
55
+ def get_loc(self, *dims):
49
56
  """Get the location (x, y) attributes of the AircraftPosition."""
50
- return self.get(*self.dims)
57
+ return self.get(*self.get_dims(*dims))
51
58
 
52
- def get_vel(self):
59
+ def get_vel(self, *dims):
53
60
  """Get the velocity attributes (dx, dy) of the AircraftPosition."""
54
- return self.get(*['d'+d for d in self.dims])
61
+ return self.get(*['d'+d for d in self.get_dims(*dims)])
55
62
 
56
- def find_direction(self):
63
+ def find_direction(self, *dims):
57
64
  """
58
65
  Find the (unit vector) direction from the x,y to goal_x, goal_y.
59
66
 
@@ -71,22 +78,22 @@ class AircraftPosition(State):
71
78
  >>> ap.find_direction()
72
79
  array([0., 1.])
73
80
  """
74
- dist = self.calc_dist()
81
+ dist = self.calc_dist(*dims)
75
82
  if dist > 0.0:
76
83
  return self.calc_vector_dist()/self.calc_dist()
77
84
  else:
78
85
  return np.zeros(len(self.dims))
79
86
 
80
- def calc_vector_dist(self):
87
+ def calc_vector_dist(self, *dims):
81
88
  """Calculate the vector distance from x,y to goal_x, goal_y."""
82
- return self.get_goal() - self.get_loc()
89
+ return self.get_goal(*dims) - self.get_loc(*dims)
83
90
 
84
- def calc_dist(self):
91
+ def calc_dist(self, *dims):
85
92
  """Calculate the scalar distance from x,y to goal_x, goal_y."""
86
- vector_dist = self.calc_vector_dist()
93
+ vector_dist = self.calc_vector_dist(*dims)
87
94
  return np.sqrt(sum(vector_dist**2))
88
95
 
89
- def at_goal(self):
96
+ def at_goal(self, *dims):
90
97
  """
91
98
  Determine if the aircraft is at its goal location.
92
99
 
@@ -99,17 +106,17 @@ class AircraftPosition(State):
99
106
  >>> ap.at_goal()
100
107
  True
101
108
  """
102
- return all(self.get_goal() == self.get_loc())
109
+ return all(self.get_goal(*dims) == self.get_loc(*dims))
103
110
 
104
- def in_range(self, dist_range=10.0):
111
+ def in_range(self, *dims, dist_range=10.0):
105
112
  """Determine if the aircraft is in the range of its goal location."""
106
- return self.calc_dist() <= dist_range
113
+ return self.calc_dist(*dims) <= dist_range
107
114
 
108
- def calc_dist_to_travel(self, dist_range=10.0):
115
+ def calc_dist_to_travel(self, *dims, dist_range=10.0):
109
116
  """Determine the distance to the goal location (under max dist dist_range)."""
110
- return np.min([dist_range, self.calc_dist()])
117
+ return np.min([dist_range, self.calc_dist(*dims)])
111
118
 
112
- def update_dist_to_travel(self, maxvel=10.0):
119
+ def update_dist_to_travel(self, *dims, maxvel=10.0):
113
120
  """
114
121
  Update dx, dy to reflect goal location (travelling at max velocity).
115
122
 
@@ -128,14 +135,14 @@ class AircraftPosition(State):
128
135
  >>> ap
129
136
  AircraftPosition(x=0.0, goal_x=10.0, dx=10.0, y=0.0, goal_y=0.0, dy=0.0)
130
137
  """
131
- if self.in_range(dist_range=maxvel):
132
- vel = self.calc_dist()
138
+ if self.in_range(*dims, dist_range=maxvel):
139
+ vel = self.calc_dist(*dims)
133
140
  else:
134
141
  vel = maxvel
135
- vels = vel * self.find_direction()
136
- self.put(**{'d'+dim: vels[i] for i, dim in enumerate(self.dims)})
142
+ vels = vel * self.find_direction(*dims)
143
+ self.put(**{'d'+dim: vels[i] for i, dim in enumerate(self.get_dims(*dims))})
137
144
 
138
- def update_position(self, maxvel=10.0):
145
+ def update_position(self, *dims, maxvel=10.0):
139
146
  """
140
147
  Update x, y to reflect progress towards the goal location at maxvel.
141
148
 
@@ -157,12 +164,12 @@ class AircraftPosition(State):
157
164
  >>> ap
158
165
  AircraftPosition(x=10.0, goal_x=10.0, dx=0.0, y=10.0, goal_y=10.0, dy=0.0)
159
166
  """
160
- self.update_dist_to_travel(maxvel=maxvel)
161
- self.increment_position()
167
+ self.update_dist_to_travel(*dims, maxvel=maxvel)
168
+ self.increment_position(*dims)
162
169
 
163
- def increment_position(self):
170
+ def increment_position(self, *dims):
164
171
  """Increment position (x,y) by (dx, dy)."""
165
- self.inc(**{dim: getattr(self, 'd'+dim) for dim in self.dims})
172
+ self.inc(**{dim: getattr(self, 'd'+dim) for dim in self.get_dims(*dims)})
166
173
 
167
174
 
168
175
  class AircraftPosition3(AircraftPosition):
@@ -148,9 +148,9 @@ class ContingencyControlFlight(ControlFlight):
148
148
  """Set new landing location in depletion scenario."""
149
149
  ts = self.trajectories.s.copy()
150
150
  ts.assign(self.environment.c.start, "goal_x", "goal_y")
151
- dist_to_start = ts.calc_dist_to_travel(1000)
151
+ dist_to_start = ts.calc_dist_to_travel(dist_range=1000)
152
152
  ts.assign(self.environment.c.end, "goal_x", "goal_y")
153
- dist_to_end = ts.calc_dist_to_travel(1000)
153
+ dist_to_end = ts.calc_dist_to_travel(dist_range=1000)
154
154
  if dist_to_end < self.electricity.s.charge:
155
155
  self.s.endpt = self.environment.c.end
156
156
  elif dist_to_start < self.electricity.s.charge:
@@ -165,7 +165,7 @@ class Threat(GeomPoint):
165
165
 
166
166
  def update_position(self):
167
167
  """Update position given known speed."""
168
- self.s.update_position(self.s.buffer_speed)
168
+ self.s.update_position(maxvel=self.s.buffer_speed)
169
169
 
170
170
  def get_shapely_args(self):
171
171
  return (self.s.x, self.s.y)
@@ -322,6 +322,7 @@ class Drone(DroneRural):
322
322
  ('taxi', 12, 20)),
323
323
  end_time=30,
324
324
  units='min',
325
+ use_local=False,
325
326
  dt=0.1)
326
327
 
327
328
  def init_architecture(self, **kwargs):
@@ -499,6 +500,9 @@ if __name__ == "__main__":
499
500
  # UrbanDroneEnvironment("a")
500
501
  # PlanPath._init_environment("a")
501
502
  # p = PlanPath("test", {})
503
+ mdl = Drone(sp={'end_condition': 'scenario_finished', 'dt': 0.1}, t={'local_dt': 0.1})
504
+ results_fault, hist_fault = propagate.one_fault(mdl, "affect_dof.ca.comps.lr", "mechbreak", time=1.4)
505
+ fig, ax = plot_env_with_traj_z(hist_fault, mdl, title = "Urban Drone Trajectory")
502
506
 
503
507
  e = UrbanDroneEnvironment("env")
504
508
  e.c.show({"height": {}})
@@ -48,7 +48,7 @@ class StochasticPumpTests(unittest.TestCase, CommonTests):
48
48
  49.32124529702974,
49
49
  0.31327201999190035,
50
50
  21.386958080811567,
51
- 3.3311929677576186,
51
+ 3.331196298953918,
52
52
  9.066120598345039,
53
53
  131.79987407014573,
54
54
  5.81402243889764,
@@ -277,7 +277,7 @@ class StochasticPumpTests(unittest.TestCase, CommonTests):
277
277
 
278
278
  if __name__ == '__main__':
279
279
  # suite = unittest.TestSuite()
280
- # suite.addTest(StochasticPumpTests("test_model_copy_same"))
280
+ # suite.addTest(StochasticPumpTests("test_stochastic_pdf"))
281
281
  # suite.addTest(StochasticPumpTests("test_save_load_nominalapproach_indiv"))
282
282
  # runner = unittest.TextTestRunner()
283
283
  # runner.run(suite)
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fmdtools
3
- Version: 2.5.2
3
+ Version: 2.5.4
4
4
  Summary: System resilience modelling, simulation, and assessment in Python
5
5
  Author: fmdtools Developers
6
6
  Maintainer-email: Daniel Hulse <daniel.e.hulse@nasa.gov>
7
- License: fmdtools version 2.5.2
7
+ License: fmdtools version 2.5.4
8
8
  ---------------------------
9
9
  Apache License
10
10
  Version 2.0, January 2004
@@ -394,7 +394,7 @@ More guidance on development installations is provided in [How to Contribute](ht
394
394
  Note that this version reflects the latest version on the ``main`` git branch along with any local changes. To use a particular version of ``fmdtools`` in the context of a development install you can checkout the tag corresponding to the version you want, e.g.:
395
395
 
396
396
  ```
397
- git checkout v2.5.2
397
+ git checkout v2.5.4
398
398
  ```
399
399
 
400
400
 
@@ -548,8 +548,8 @@ To cite a particular version of the fmdtools, you may use:
548
548
  author = {{NASA}},
549
549
  title = {fmdtools},
550
550
  url = {https://github.com/nasa/fmdtools},
551
- version = {2.5.2},
552
- date = {2026-9-28},
551
+ version = {2.5.4},
552
+ date = {2026-10-5},
553
553
  }
554
554
  ```
555
555
 
@@ -127,31 +127,112 @@ src/fmdtools/sim/scenario.py
127
127
  src/fmdtools/sim/search.py
128
128
  tests/__init__.py
129
129
  tests/common.py
130
+ tests/test_appended_history_snapshots.py
131
+ tests/test_approach_joint_domains.py
132
+ tests/test_approach_model_timestep.py
133
+ tests/test_approach_phase_map_ownership.py
134
+ tests/test_array_differences.py
130
135
  tests/test_array_random_states.py
136
+ tests/test_array_result_equality.py
137
+ tests/test_array_state_limits.py
138
+ tests/test_bootstrap_statistic_arguments.py
139
+ tests/test_bounded_array_increments.py
131
140
  tests/test_calc_metric_ci_constant_outputs.py
141
+ tests/test_callable_parameter_domains.py
142
+ tests/test_choice_draw_mass.py
143
+ tests/test_choice_probability_precision.py
144
+ tests/test_common.py
132
145
  tests/test_comparison_groups.py
133
146
  tests/test_confidence_history_clipping.py
147
+ tests/test_coordinate_cell_boundaries.py
148
+ tests/test_default_fault_sample_timestep.py
149
+ tests/test_degradation_path_boundaries.py
150
+ tests/test_dirichlet_batches.py
151
+ tests/test_disconnected_graph_robustness.py
152
+ tests/test_discrete_sample_sizes.py
134
153
  tests/test_error_history_plots.py
154
+ tests/test_exact_nominal_expectation.py
155
+ tests/test_explicit_result_file_formats.py
156
+ tests/test_exponential_rayleigh_draw_sizes.py
157
+ tests/test_factor_sort_priority.py
158
+ tests/test_fault_history_paths.py
135
159
  tests/test_fault_sample_without_nominal.py
136
160
  tests/test_fault_time_without_traces.py
161
+ tests/test_fault_weight_arrays.py
162
+ tests/test_faulty_scenario_selection.py
163
+ tests/test_fmea_input_ownership.py
164
+ tests/test_gamma_sampling_density.py
165
+ tests/test_generated_history_phase_timestep.py
137
166
  tests/test_geom.py
138
167
  tests/test_graph.py
168
+ tests/test_graph_edge_comparison.py
139
169
  tests/test_hierarchical.py
170
+ tests/test_history_phase_identifiers.py
140
171
  tests/test_history_standard_deviation.py
172
+ tests/test_independent_single_replicate_seeds.py
173
+ tests/test_individual_result_paths.py
174
+ tests/test_integer_draw_mass.py
175
+ tests/test_join_phase_timestep.py
141
176
  tests/test_join_phasemaps.py
177
+ tests/test_joint_fault_metadata_order.py
178
+ tests/test_joint_fault_phase_map.py
142
179
  tests/test_joint_fault_weights.py
180
+ tests/test_json_result_arrays.py
181
+ tests/test_location_scale_draw_sizes.py
182
+ tests/test_lognormal_defaults.py
183
+ tests/test_metric_weight_alignment.py
184
+ tests/test_mixed_shape_degradation.py
185
+ tests/test_multidimensional_state_comparison.py
186
+ tests/test_multinomial_sampling_arguments.py
187
+ tests/test_multivariate_hypergeometric_options.py
188
+ tests/test_multivariate_normal_arguments.py
189
+ tests/test_named_default_restoration.py
190
+ tests/test_named_metric_rounding.py
191
+ tests/test_nearest_point_exclusion.py
192
+ tests/test_nested_history_summary.py
143
193
  tests/test_nested_nominal_groups.py
194
+ tests/test_nested_parameter_constraints.py
195
+ tests/test_nested_scenario_parameters.py
144
196
  tests/test_nesting_custom_separators.py
197
+ tests/test_not_equal_constraints.py
198
+ tests/test_object_history_copy.py
145
199
  tests/test_packages.py
200
+ tests/test_parameter_grid_bounds.py
201
+ tests/test_parameter_sample_defaults.py
146
202
  tests/test_parameter_sample_seeds.py
147
203
  tests/test_parameter_subsampling_types.py
204
+ tests/test_pareto_density.py
148
205
  tests/test_pathplan.py
206
+ tests/test_permutation_axis_probabilities.py
207
+ tests/test_permuted_slice_probabilities.py
208
+ tests/test_phase_map_default_isolation.py
149
209
  tests/test_phase_timestep.py
150
210
  tests/test_pruned_fault_times.py
211
+ tests/test_ragged_confidence_histories.py
212
+ tests/test_rand.py
213
+ tests/test_random_combination_types.py
151
214
  tests/test_result.py
215
+ tests/test_result_constructor_input.py
216
+ tests/test_result_folder_selection.py
152
217
  tests/test_sample.py
153
218
  tests/test_scenario.py
154
219
  tests/test_scenario_defaults.py
220
+ tests/test_shape_distribution_sizes.py
221
+ tests/test_simple_problem_vector_inputs.py
222
+ tests/test_simulation_exposure.py
223
+ tests/test_standard_draw_arguments.py
224
+ tests/test_standard_t_density.py
225
+ tests/test_string_state_histories.py
155
226
  tests/test_table_factor_indices.py
156
227
  tests/test_time.py
157
- tests/test_uniform_density.py
228
+ tests/test_time_reset.py
229
+ tests/test_triangular_arraylike_parameters.py
230
+ tests/test_tuple_table_factors.py
231
+ tests/test_uniform_density.py
232
+ tests/test_unit_random_density.py
233
+ tests/test_vonmises_density.py
234
+ tests/test_wald_density.py
235
+ tests/test_weighted_percent_metrics.py
236
+ tests/test_zero_fault_start_times.py
237
+ tests/test_zero_time_objectives.py
238
+ tests/test_zero_timer_increment.py
@@ -18,7 +18,7 @@ classifiers = [
18
18
  description = "System resilience modelling, simulation, and assessment in Python"
19
19
  license = {file="LICENSE"}
20
20
  name = "fmdtools"
21
- version="2.5.2"
21
+ version="2.5.4"
22
22
  readme = "README.md"
23
23
  requires-python = ">=3.12"
24
24
 
@@ -22,6 +22,6 @@ import fmdtools.sim as sim
22
22
  import fmdtools.analyze as analyze
23
23
 
24
24
  __all__ = ['analyze', 'sim', 'define']
25
- __version__ = '2.5.2'
25
+ __version__ = '2.5.4'
26
26
  __copyright__ = "Copyright 2024 United States Government"
27
27
  __license__ = "Apache-2.0"
@@ -35,6 +35,7 @@ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
35
35
  CONDITIONS OF ANY KIND, either express or implied. See the License for the
36
36
  specific language governing permissions and limitations under the License.
37
37
  """
38
+ import math
38
39
  import numpy as np
39
40
  import os
40
41
  import matplotlib.pyplot as plt
@@ -42,6 +43,7 @@ from matplotlib import rcParams
42
43
  import inspect
43
44
  from scipy.stats import bootstrap
44
45
  from fmdtools.define.base import filter_kwargs, round_float
46
+ from functools import partial
45
47
 
46
48
 
47
49
  plt.rcParams['pdf.fonttype'] = 42
@@ -180,11 +182,11 @@ def create_indiv_filename(filename, indiv_id, splitchar='_'):
180
182
  --------
181
183
  >>> create_indiv_filename("hi.csv", "4")
182
184
  'hi_4.csv'
185
+ >>> create_indiv_filename("runs.v1/results.v2.csv", "4", splitchar="/")
186
+ 'runs.v1/results.v2/4.csv'
183
187
  """
184
- filename_parts = filename.split(".")
185
- filename_parts.insert(1, '.')
186
- filename_parts.insert(1, splitchar+indiv_id)
187
- return "".join(filename_parts)
188
+ stem, extension = os.path.splitext(filename)
189
+ return stem + splitchar + indiv_id + extension
188
190
 
189
191
 
190
192
  def load_folder(folder, filetype):
@@ -198,20 +200,17 @@ def load_folder(folder, filetype):
198
200
  folder : str
199
201
  Name of the folder. Must be in the current directory
200
202
  filetype : str
201
- Type of files in the folder ('pickle', 'csv', or 'json')
203
+ Type of files in the folder ('npz', 'csv', or 'json'). Other extensions
204
+ and subdirectories are ignored; matching files are not searched recursively.
202
205
 
203
206
  Returns
204
207
  -------
205
208
  files_to_read : list
206
209
  files to load for results/mdlhists.
207
210
  """
208
- files = os.listdir(folder)
209
- files_toread = []
210
- for file in files:
211
- read_filetype = auto_filetype(file)
212
- if read_filetype == filetype:
213
- files_toread.append(file)
214
- return files_toread
211
+ return [file for file in os.listdir(folder)
212
+ if file.endswith('.' + filetype)
213
+ and os.path.isfile(os.path.join(folder, file))]
215
214
 
216
215
 
217
216
  def metric_preamble(data, dtype=None, rates=None, r_dtype=None, r_norm=False):
@@ -255,7 +254,8 @@ def calc_metric(data, method=np.average, args=(), axis=None, dtype=None,
255
254
  r_norm : bool, optional
256
255
  Whether to normalize rates. The default is False.
257
256
  round_value : bool, optional
258
- Whether to round the value. Default is True.
257
+ Whether to round the final value, including named metrics.
258
+ Applied once after evaluation. Default is True.
259
259
  res : float
260
260
  Resolution to round to. Default is 1e-6.
261
261
  min_r : int
@@ -283,8 +283,9 @@ def calc_metric(data, method=np.average, args=(), axis=None, dtype=None,
283
283
  """
284
284
  if isinstance(method, str):
285
285
  method = eval("calc_"+method)
286
+ # Named helpers call calc_metric again; round only the final result.
286
287
  metric = method(data, args=args, axis=axis, dtype=dtype, rates=rates,
287
- r_dtype=r_dtype, r_norm=r_norm, **kwargs)
288
+ r_dtype=r_dtype, r_norm=r_norm, round_value=False, **kwargs)
288
289
  else:
289
290
  vals = metric_preamble(data, dtype, rates, r_dtype, r_norm)
290
291
  metric = method(vals, *args, **filter_kwargs(method, **kwargs, axis=axis))
@@ -309,7 +310,8 @@ def calc_metric_ci(data, method=np.average, return_anyway=False, interval=None,
309
310
  axis : int
310
311
  Axis along which to calculate the confidence interval(s). The default is 0.
311
312
  **kwargs : kwargs
312
- Keyword arguments to calc_metric or scipy.bootstrap.
313
+ Keyword arguments to the statistic, preprocessing, or scipy.bootstrap.
314
+ Statistic arguments are used for both the point estimate and resamples.
313
315
 
314
316
  Returns
315
317
  -------
@@ -339,15 +341,16 @@ def calc_metric_ci(data, method=np.average, return_anyway=False, interval=None,
339
341
  val = np.take(vals, [0], axis=axis)
340
342
  if "weights" in kwargs and kwargs['weights'] is not None:
341
343
  raise Exception("Weights not able to be used w- bootstrap--use rates instead.")
342
- met_val = method(vals, axis=axis, **filter_kwargs(method, **kwargs))
344
+ statistic = partial(method, **filter_kwargs(method, **kwargs))
345
+ met_val = statistic(vals, axis=axis)
343
346
  vals_vary = vals == val
344
347
  # Preserve the existing policy for globally identical data.
345
348
  if not np.all(vals == vals.flat[0]):
346
349
  if np.any(np.all(vals_vary, axis=axis)):
347
350
  # use more robust/basic algorithm if some indices don't vary
348
- bs = bootstrap([vals], method, axis=axis, method="basic", **bs_kwar)
351
+ bs = bootstrap([vals], statistic, axis=axis, method="basic", **bs_kwar)
349
352
  else:
350
- bs = bootstrap([vals], method, axis=axis, **bs_kwar)
353
+ bs = bootstrap([vals], statistic, axis=axis, **bs_kwar)
351
354
  return met_val, bs.confidence_interval.low, bs.confidence_interval.high
352
355
  elif return_anyway:
353
356
  return met_val, met_val, met_val
@@ -355,10 +358,26 @@ def calc_metric_ci(data, method=np.average, return_anyway=False, interval=None,
355
358
  raise Exception("All data are the same!")
356
359
 
357
360
 
361
+ def _uniform_rate(data, axis=None):
362
+ """Assign uniform mass over reduced axes, preserving unreduced dimensions."""
363
+ shape = np.shape(data)
364
+ if axis is None:
365
+ count = np.size(data)
366
+ elif not shape:
367
+ count = 1 # NumPy's subsequent reduction validates scalar axes.
368
+ else:
369
+ axes = axis if isinstance(axis, tuple) else (axis,)
370
+ count = math.prod(shape[a] for a in axes)
371
+ return 1 / count
372
+
373
+
358
374
  def calc_rate(data, rates=None, weights=None, **kwargs):
359
375
  """
360
376
  Calculate a rate of a non-zero value in data using calc_metric.
361
377
 
378
+ With no explicit rates, use equal probability over the selected axis/axes.
379
+ Other dimensions, such as history time, do not dilute each reduced rate.
380
+
362
381
  Examples
363
382
  --------
364
383
  >>> calc_rate([0, 10, 0]) # defaults to equal rate
@@ -367,21 +386,24 @@ def calc_rate(data, rates=None, weights=None, **kwargs):
367
386
  np.float64(0.2)
368
387
  """
369
388
  if rates is None:
370
- rates = 1/np.size(data)
389
+ rates = _uniform_rate(data, kwargs.get('axis'))
371
390
  kwar = {**kwargs, 'method': np.sum, 'dtype': bool, 'rates': rates}
372
391
  return calc_metric(data, **kwar)
373
392
 
374
393
 
375
394
  def calc_percent(data, weights=None, rates=None, **kwargs):
376
395
  """
377
- Calculate a percent of a non-zero value in data using calc_metric.
396
+ Calculate a weighted fraction of non-zero values using calc_metric.
397
+
398
+ Weights are passed to the averaging method; omitted weights give each value
399
+ equal influence. Rates remain separate from percentage weights.
378
400
 
379
401
  Examples
380
402
  --------
381
403
  >>> calc_percent([0, 10, 0])
382
404
  np.float64(0.333333)
383
405
  """
384
- return calc_metric(data, **{**kwargs, 'dtype': bool})
406
+ return calc_metric(data, **{**kwargs, 'dtype': bool, 'weights': weights})
385
407
 
386
408
 
387
409
  def calc_total(data, weights=None, **kwargs):
@@ -402,6 +424,9 @@ def calc_expected(data, rates=None, weights=None, **kwargs):
402
424
  """
403
425
  Calculate the expected value of given data using calc_metric.
404
426
 
427
+ With no explicit rates, use equal probability over the selected axis/axes.
428
+ Explicit rates retain their existing weighted-sum interpretation.
429
+
405
430
  Examples
406
431
  --------
407
432
  >>> calc_expected([0, 5, 10]) # defaults to average
@@ -410,7 +435,7 @@ def calc_expected(data, rates=None, weights=None, **kwargs):
410
435
  np.float64(3.5)
411
436
  """
412
437
  if rates is None:
413
- rates = 1/np.size(data)
438
+ rates = _uniform_rate(data, kwargs.get('axis'))
414
439
  return calc_metric(data, **{**kwargs, 'method': np.sum, 'rates': rates})
415
440
 
416
441
 
@@ -841,6 +841,9 @@ class Graph(object):
841
841
  """
842
842
  Compute robustness coefficient of graph representation of model mdl.
843
843
 
844
+ Each trial removes nodes from the entire graph, including disconnected
845
+ components, to integrate the complete largest-component curve.
846
+
844
847
  Parameters
845
848
  ----------
846
849
  trials : int
@@ -865,9 +868,9 @@ class Graph(object):
865
868
  N = float(len(tmp))
866
869
  largestCC = max(nx.connected_components(tmp), key=len)
867
870
  s = [float(len(largestCC))]
868
- rs = rng.choice(range(int(s[0])), int(s[0]), replace=False)
871
+ rs = rng.choice(range(int(N)), int(N), replace=False)
869
872
  nodes = list(g)
870
- for i in range(int(s[0])-1):
873
+ for i in range(int(N)-1):
871
874
  tmp.remove_node(nodes[rs[i]])
872
875
  largestCC = max(nx.connected_components(tmp), key=len)
873
876
  s.append(float(len(largestCC)))
@@ -1228,6 +1231,9 @@ class Graph(object):
1228
1231
  Useful for comparing different model variants, design alternatives, or
1229
1232
  tracking how model structure evolves across versions.
1230
1233
 
1234
+ When both graphs are undirected, edge endpoint order is ignored.
1235
+ Added and removed edges retain the endpoint tuples from their source graph.
1236
+
1231
1237
  Parameters
1232
1238
  ----------
1233
1239
  other_graph : Graph
@@ -1267,6 +1273,10 @@ class Graph(object):
1267
1273
  nodes_other = set(other_graph.g.nodes())
1268
1274
  edges_this = set(self.g.edges())
1269
1275
  edges_other = set(other_graph.g.edges())
1276
+ if not (self.g.is_directed() or other_graph.g.is_directed()):
1277
+ # Give shared undirected edges the same orientation for comparison.
1278
+ edges_other = {edge[::-1] if edge[::-1] in edges_this else edge
1279
+ for edge in edges_other}
1270
1280
 
1271
1281
  nodes_added = nodes_other - nodes_this
1272
1282
  nodes_removed = nodes_this - nodes_other