diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 0000000..77c12d9 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 95f963152e56a34d2450a8d3a9cdb5eb +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.doctrees/build/apidocs/obsidian.acquisition.botorch.doctree b/.doctrees/build/apidocs/obsidian.acquisition.botorch.doctree new file mode 100644 index 0000000..3b9205a Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.acquisition.botorch.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.acquisition.custom.doctree b/.doctrees/build/apidocs/obsidian.acquisition.custom.doctree new file mode 100644 index 0000000..90810c9 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.acquisition.custom.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.acquisition.doctree b/.doctrees/build/apidocs/obsidian.acquisition.doctree new file mode 100644 index 0000000..c12bedf Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.acquisition.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.analysis.calc_ofat_ranges.doctree b/.doctrees/build/apidocs/obsidian.campaign.analysis.calc_ofat_ranges.doctree new file mode 100644 index 0000000..c7310b4 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.analysis.calc_ofat_ranges.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.analysis.doctree b/.doctrees/build/apidocs/obsidian.campaign.analysis.doctree new file mode 100644 index 0000000..597e7ec Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.analysis.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.analysis.plot_interactions.doctree b/.doctrees/build/apidocs/obsidian.campaign.analysis.plot_interactions.doctree new file mode 100644 index 0000000..03199e9 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.analysis.plot_interactions.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.analysis.plot_ofat_ranges.doctree b/.doctrees/build/apidocs/obsidian.campaign.analysis.plot_ofat_ranges.doctree new file mode 100644 index 0000000..a78f91e Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.analysis.plot_ofat_ranges.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.analysis.sensitivity.doctree b/.doctrees/build/apidocs/obsidian.campaign.analysis.sensitivity.doctree new file mode 100644 index 0000000..6250ba7 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.analysis.sensitivity.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.campaign.Campaign.doctree b/.doctrees/build/apidocs/obsidian.campaign.campaign.Campaign.doctree new file mode 100644 index 0000000..c4c0af3 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.campaign.Campaign.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.campaign.doctree b/.doctrees/build/apidocs/obsidian.campaign.campaign.doctree new file mode 100644 index 0000000..14e7e22 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.campaign.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.doctree b/.doctrees/build/apidocs/obsidian.campaign.doctree new file mode 100644 index 0000000..cd04346 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.explainer.Explainer.doctree b/.doctrees/build/apidocs/obsidian.campaign.explainer.Explainer.doctree new file mode 100644 index 0000000..4a43624 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.explainer.Explainer.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.campaign.explainer.doctree b/.doctrees/build/apidocs/obsidian.campaign.explainer.doctree new file mode 100644 index 0000000..cd04f53 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.campaign.explainer.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.constraints.doctree b/.doctrees/build/apidocs/obsidian.constraints.doctree new file mode 100644 index 0000000..6bed160 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.constraints.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.constraints.input.doctree b/.doctrees/build/apidocs/obsidian.constraints.input.doctree new file mode 100644 index 0000000..27a02d9 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.constraints.input.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.constraints.output.doctree b/.doctrees/build/apidocs/obsidian.constraints.output.doctree new file mode 100644 index 0000000..67a45a1 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.constraints.output.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.exceptions.doctree b/.doctrees/build/apidocs/obsidian.exceptions.doctree new file mode 100644 index 0000000..f1b337c Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.exceptions.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.doctree new file mode 100644 index 0000000..b92c7e4 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.ackley.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.ackley.doctree new file mode 100644 index 0000000..c2b102a Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.ackley.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.branin_currin.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.branin_currin.doctree new file mode 100644 index 0000000..bd88fa3 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.branin_currin.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.cornered_parab.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.cornered_parab.doctree new file mode 100644 index 0000000..763cca9 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.cornered_parab.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.doctree new file mode 100644 index 0000000..3665697 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.paraboloid.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.paraboloid.doctree new file mode 100644 index 0000000..0044854 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.paraboloid.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.perm.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.perm.doctree new file mode 100644 index 0000000..684d16e Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.perm.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.rosenbrock.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.rosenbrock.doctree new file mode 100644 index 0000000..0b1ed74 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.rosenbrock.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.shifted_parab.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.shifted_parab.doctree new file mode 100644 index 0000000..4ed5f24 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.shifted_parab.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.sixhump_camel.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.sixhump_camel.doctree new file mode 100644 index 0000000..068bf8f Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.sixhump_camel.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.threehump_camel.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.threehump_camel.doctree new file mode 100644 index 0000000..60b6838 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.threehump_camel.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.two_leaves.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.two_leaves.doctree new file mode 100644 index 0000000..a36f718 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.geometric.two_leaves.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.Km_func.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.Km_func.doctree new file mode 100644 index 0000000..b00453b Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.Km_func.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.OT_simulator.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.OT_simulator.doctree new file mode 100644 index 0000000..4ade9a0 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.OT_simulator.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.Vm_func.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.Vm_func.doctree new file mode 100644 index 0000000..216c625 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.Vm_func.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.doctree new file mode 100644 index 0000000..8e9fd41 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.kI_func.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.kI_func.doctree new file mode 100644 index 0000000..95e4227 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.kI_func.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.response_1.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.response_1.doctree new file mode 100644 index 0000000..c4d3616 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.response_1.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.response_2.doctree b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.response_2.doctree new file mode 100644 index 0000000..a0986a9 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.benchmark.optithon.response_2.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.design.ExpDesigner.doctree b/.doctrees/build/apidocs/obsidian.experiment.design.ExpDesigner.doctree new file mode 100644 index 0000000..0726777 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.design.ExpDesigner.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.design.doctree b/.doctrees/build/apidocs/obsidian.experiment.design.doctree new file mode 100644 index 0000000..76f54d9 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.design.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.doctree b/.doctrees/build/apidocs/obsidian.experiment.doctree new file mode 100644 index 0000000..05410aa Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.simulator.Simulator.doctree b/.doctrees/build/apidocs/obsidian.experiment.simulator.Simulator.doctree new file mode 100644 index 0000000..700224c Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.simulator.Simulator.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.simulator.doctree b/.doctrees/build/apidocs/obsidian.experiment.simulator.doctree new file mode 100644 index 0000000..f6f64e8 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.simulator.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.utils.doctree b/.doctrees/build/apidocs/obsidian.experiment.utils.doctree new file mode 100644 index 0000000..5cdde19 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.utils.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.experiment.utils.factorial_DOE.doctree b/.doctrees/build/apidocs/obsidian.experiment.utils.factorial_DOE.doctree new file mode 100644 index 0000000..51ffcc6 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.experiment.utils.factorial_DOE.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.objectives.base.doctree b/.doctrees/build/apidocs/obsidian.objectives.base.doctree new file mode 100644 index 0000000..92e186a Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.objectives.base.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.objectives.custom.doctree b/.doctrees/build/apidocs/obsidian.objectives.custom.doctree new file mode 100644 index 0000000..8729c9b Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.objectives.custom.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.objectives.doctree b/.doctrees/build/apidocs/obsidian.objectives.doctree new file mode 100644 index 0000000..0302ca5 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.objectives.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.objectives.scalarize.doctree b/.doctrees/build/apidocs/obsidian.objectives.scalarize.doctree new file mode 100644 index 0000000..aaddcd7 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.objectives.scalarize.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.objectives.sequence.doctree b/.doctrees/build/apidocs/obsidian.objectives.sequence.doctree new file mode 100644 index 0000000..b661ece Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.objectives.sequence.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.optimizer.base.Optimizer.doctree b/.doctrees/build/apidocs/obsidian.optimizer.base.Optimizer.doctree new file mode 100644 index 0000000..3c14421 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.optimizer.base.Optimizer.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.optimizer.base.doctree b/.doctrees/build/apidocs/obsidian.optimizer.base.doctree new file mode 100644 index 0000000..a6460f2 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.optimizer.base.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.optimizer.bayesian.BayesianOptimizer.doctree b/.doctrees/build/apidocs/obsidian.optimizer.bayesian.BayesianOptimizer.doctree new file mode 100644 index 0000000..8d47fcd Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.optimizer.bayesian.BayesianOptimizer.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.optimizer.bayesian.doctree b/.doctrees/build/apidocs/obsidian.optimizer.bayesian.doctree new file mode 100644 index 0000000..abf9cf4 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.optimizer.bayesian.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.optimizer.doctree b/.doctrees/build/apidocs/obsidian.optimizer.doctree new file mode 100644 index 0000000..4f8e309 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.optimizer.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.base.Parameter.doctree b/.doctrees/build/apidocs/obsidian.parameters.base.Parameter.doctree new file mode 100644 index 0000000..5a43fee Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.base.Parameter.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.base.doctree b/.doctrees/build/apidocs/obsidian.parameters.base.doctree new file mode 100644 index 0000000..fd94e0b Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.base.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.continuous.Param_Continuous.doctree b/.doctrees/build/apidocs/obsidian.parameters.continuous.Param_Continuous.doctree new file mode 100644 index 0000000..bb05219 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.continuous.Param_Continuous.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.continuous.Param_Observational.doctree b/.doctrees/build/apidocs/obsidian.parameters.continuous.Param_Observational.doctree new file mode 100644 index 0000000..0a54f61 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.continuous.Param_Observational.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.continuous.doctree b/.doctrees/build/apidocs/obsidian.parameters.continuous.doctree new file mode 100644 index 0000000..bad46ec Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.continuous.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Categorical.doctree b/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Categorical.doctree new file mode 100644 index 0000000..087f8ae Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Categorical.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Discrete.doctree b/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Discrete.doctree new file mode 100644 index 0000000..4d9e322 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Discrete.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Discrete_Numeric.doctree b/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Discrete_Numeric.doctree new file mode 100644 index 0000000..536a511 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Discrete_Numeric.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Ordinal.doctree b/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Ordinal.doctree new file mode 100644 index 0000000..0fcaac8 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.discrete.Param_Ordinal.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.discrete.Task.doctree b/.doctrees/build/apidocs/obsidian.parameters.discrete.Task.doctree new file mode 100644 index 0000000..6aa2156 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.discrete.Task.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.discrete.doctree b/.doctrees/build/apidocs/obsidian.parameters.discrete.doctree new file mode 100644 index 0000000..6f1b05d Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.discrete.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.doctree b/.doctrees/build/apidocs/obsidian.parameters.doctree new file mode 100644 index 0000000..e54affe Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.param_space.ParamSpace.doctree b/.doctrees/build/apidocs/obsidian.parameters.param_space.ParamSpace.doctree new file mode 100644 index 0000000..a6d33d2 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.param_space.ParamSpace.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.param_space.doctree b/.doctrees/build/apidocs/obsidian.parameters.param_space.doctree new file mode 100644 index 0000000..c30e39d Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.param_space.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.targets.Target.doctree b/.doctrees/build/apidocs/obsidian.parameters.targets.Target.doctree new file mode 100644 index 0000000..1eef058 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.targets.Target.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.targets.doctree b/.doctrees/build/apidocs/obsidian.parameters.targets.doctree new file mode 100644 index 0000000..7206c12 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.targets.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.transforms.Identity_Scaler.doctree b/.doctrees/build/apidocs/obsidian.parameters.transforms.Identity_Scaler.doctree new file mode 100644 index 0000000..a18f4d6 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.transforms.Identity_Scaler.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.transforms.Logit_Scaler.doctree b/.doctrees/build/apidocs/obsidian.parameters.transforms.Logit_Scaler.doctree new file mode 100644 index 0000000..30a306f Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.transforms.Logit_Scaler.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.transforms.Standard_Scaler.doctree b/.doctrees/build/apidocs/obsidian.parameters.transforms.Standard_Scaler.doctree new file mode 100644 index 0000000..d732a0d Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.transforms.Standard_Scaler.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.transforms.Target_Transform.doctree b/.doctrees/build/apidocs/obsidian.parameters.transforms.Target_Transform.doctree new file mode 100644 index 0000000..6b75beb Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.transforms.Target_Transform.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.transforms.doctree b/.doctrees/build/apidocs/obsidian.parameters.transforms.doctree new file mode 100644 index 0000000..2417a1a Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.transforms.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.utils.doctree b/.doctrees/build/apidocs/obsidian.parameters.utils.doctree new file mode 100644 index 0000000..41f2d53 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.utils.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.parameters.utils.transform_with_type.doctree b/.doctrees/build/apidocs/obsidian.parameters.utils.transform_with_type.doctree new file mode 100644 index 0000000..64ea01f Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.parameters.utils.transform_with_type.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.plotting.branding.doctree b/.doctrees/build/apidocs/obsidian.plotting.branding.doctree new file mode 100644 index 0000000..57600c0 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.plotting.branding.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.plotting.doctree b/.doctrees/build/apidocs/obsidian.plotting.doctree new file mode 100644 index 0000000..7bfdffa Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.plotting.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.plotting.mpl.doctree b/.doctrees/build/apidocs/obsidian.plotting.mpl.doctree new file mode 100644 index 0000000..27b62af Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.plotting.mpl.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.plotting.plotly.doctree b/.doctrees/build/apidocs/obsidian.plotting.plotly.doctree new file mode 100644 index 0000000..974caa7 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.plotting.plotly.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.plotting.shap.doctree b/.doctrees/build/apidocs/obsidian.plotting.shap.doctree new file mode 100644 index 0000000..dea033c Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.plotting.shap.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.base.SurrogateModel.doctree b/.doctrees/build/apidocs/obsidian.surrogates.base.SurrogateModel.doctree new file mode 100644 index 0000000..0003cf1 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.base.SurrogateModel.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.base.doctree b/.doctrees/build/apidocs/obsidian.surrogates.base.doctree new file mode 100644 index 0000000..0be311c Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.base.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.botorch.SurrogateBoTorch.doctree b/.doctrees/build/apidocs/obsidian.surrogates.botorch.SurrogateBoTorch.doctree new file mode 100644 index 0000000..d047e80 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.botorch.SurrogateBoTorch.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.botorch.doctree b/.doctrees/build/apidocs/obsidian.surrogates.botorch.doctree new file mode 100644 index 0000000..3b10457 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.botorch.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.DKLGP.doctree b/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.DKLGP.doctree new file mode 100644 index 0000000..2b2b363 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.DKLGP.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.FlatGP.doctree b/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.FlatGP.doctree new file mode 100644 index 0000000..09da44a Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.FlatGP.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.PriorGP.doctree b/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.PriorGP.doctree new file mode 100644 index 0000000..d7e6371 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.PriorGP.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.doctree b/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.doctree new file mode 100644 index 0000000..25bec61 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.custom_GP.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.custom_torch.DNN.doctree b/.doctrees/build/apidocs/obsidian.surrogates.custom_torch.DNN.doctree new file mode 100644 index 0000000..a6f6fb2 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.custom_torch.DNN.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.custom_torch.doctree b/.doctrees/build/apidocs/obsidian.surrogates.custom_torch.doctree new file mode 100644 index 0000000..492c841 Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.custom_torch.doctree differ diff --git a/.doctrees/build/apidocs/obsidian.surrogates.doctree b/.doctrees/build/apidocs/obsidian.surrogates.doctree new file mode 100644 index 0000000..4d7724b Binary files /dev/null and b/.doctrees/build/apidocs/obsidian.surrogates.doctree differ diff --git a/.doctrees/environment.pickle b/.doctrees/environment.pickle new file mode 100644 index 0000000..4f9b1bb Binary files /dev/null and b/.doctrees/environment.pickle differ diff --git a/.doctrees/index.doctree b/.doctrees/index.doctree new file mode 100644 index 0000000..65252bf Binary files /dev/null and b/.doctrees/index.doctree differ diff --git a/.doctrees/stubs/api_docs.doctree b/.doctrees/stubs/api_docs.doctree new file mode 100644 index 0000000..389ecbf Binary files /dev/null and b/.doctrees/stubs/api_docs.doctree differ diff --git a/.doctrees/stubs/changelog.doctree b/.doctrees/stubs/changelog.doctree new file mode 100644 index 0000000..960c806 Binary files /dev/null and b/.doctrees/stubs/changelog.doctree differ diff --git a/.doctrees/stubs/contributing.doctree b/.doctrees/stubs/contributing.doctree new file mode 100644 index 0000000..136a73b Binary files /dev/null and b/.doctrees/stubs/contributing.doctree differ diff --git a/.doctrees/stubs/license.doctree b/.doctrees/stubs/license.doctree new file mode 100644 index 0000000..4dffdab Binary files /dev/null and b/.doctrees/stubs/license.doctree differ diff --git a/.doctrees/stubs/reference.doctree b/.doctrees/stubs/reference.doctree new file mode 100644 index 0000000..e0c8a95 Binary files /dev/null and b/.doctrees/stubs/reference.doctree differ diff --git a/.doctrees/stubs/tutorials.doctree b/.doctrees/stubs/tutorials.doctree new file mode 100644 index 0000000..e3c3287 Binary files /dev/null and b/.doctrees/stubs/tutorials.doctree differ diff --git a/.doctrees/stubs/tutorials/Simple single objective.doctree b/.doctrees/stubs/tutorials/Simple single objective.doctree new file mode 100644 index 0000000..fe17013 Binary files /dev/null and b/.doctrees/stubs/tutorials/Simple single objective.doctree differ diff --git a/.doctrees/stubs/wiki.doctree b/.doctrees/stubs/wiki.doctree new file mode 100644 index 0000000..7bc43b6 Binary files /dev/null and b/.doctrees/stubs/wiki.doctree differ diff --git a/.doctrees/wiki/1_APO_Workflow_and_CodeStructure.doctree b/.doctrees/wiki/1_APO_Workflow_and_CodeStructure.doctree new file mode 100644 index 0000000..9c631ff Binary files /dev/null and b/.doctrees/wiki/1_APO_Workflow_and_CodeStructure.doctree differ diff --git a/.doctrees/wiki/2_Analysis_and_Visualization.doctree b/.doctrees/wiki/2_Analysis_and_Visualization.doctree new file mode 100644 index 0000000..72946ea Binary files /dev/null and b/.doctrees/wiki/2_Analysis_and_Visualization.doctree differ diff --git a/.doctrees/wiki/3_Data.doctree b/.doctrees/wiki/3_Data.doctree new file mode 100644 index 0000000..f4be148 Binary files /dev/null and b/.doctrees/wiki/3_Data.doctree differ diff --git a/.doctrees/wiki/4_SurrogateModel.doctree b/.doctrees/wiki/4_SurrogateModel.doctree new file mode 100644 index 0000000..96e515a Binary files /dev/null and b/.doctrees/wiki/4_SurrogateModel.doctree differ diff --git a/.doctrees/wiki/5_AcquisitionFunction.doctree b/.doctrees/wiki/5_AcquisitionFunction.doctree new file mode 100644 index 0000000..3f5b910 Binary files /dev/null and b/.doctrees/wiki/5_AcquisitionFunction.doctree differ diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_modules/botorch/acquisition/active_learning.html b/_modules/botorch/acquisition/active_learning.html new file mode 100644 index 0000000..194e476 --- /dev/null +++ b/_modules/botorch/acquisition/active_learning.html @@ -0,0 +1,682 @@ + + + + + + + + + + botorch.acquisition.active_learning — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for botorch.acquisition.active_learning

+#!/usr/bin/env python3
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# This source code is licensed under the MIT license found in the
+# LICENSE file in the root directory of this source tree.
+
+r"""
+Active learning acquisition functions.
+
+.. [Seo2014activedata]
+    S. Seo, M. Wallat, T. Graepel, and K. Obermayer. Gaussian process regression:
+    Active data selection and test point rejection. IJCNN 2000.
+
+.. [Chen2014seqexpdesign]
+    X. Chen and Q. Zhou. Sequential experimental designs for stochastic kriging.
+    Winter Simulation Conference 2014.
+
+.. [Binois2017repexp]
+    M. Binois, J. Huang, R. B. Gramacy, and M. Ludkovski. Replication or
+    exploration? Sequential design for stochastic simulation experiments.
+    ArXiv 2017.
+"""
+
+from __future__ import annotations
+
+from typing import Optional
+
+import torch
+from botorch import settings
+from botorch.acquisition.acquisition import AcquisitionFunction
+from botorch.acquisition.monte_carlo import MCAcquisitionFunction
+from botorch.acquisition.objective import MCAcquisitionObjective, PosteriorTransform
+from botorch.models.model import Model
+from botorch.sampling.base import MCSampler
+from botorch.sampling.normal import SobolQMCNormalSampler
+from botorch.utils.transforms import concatenate_pending_points, t_batch_mode_transform
+from torch import Tensor
+
+
+
+[docs] +class qNegIntegratedPosteriorVariance(AcquisitionFunction): + r"""Batch Integrated Negative Posterior Variance for Active Learning. + + This acquisition function quantifies the (negative) integrated posterior variance + (excluding observation noise, computed using MC integration) of the model. + In that, it is a proxy for global model uncertainty, and thus purely focused on + "exploration", rather the "exploitation" of many of the classic Bayesian + Optimization acquisition functions. + + See [Seo2014activedata]_, [Chen2014seqexpdesign]_, and [Binois2017repexp]_. + """ + + def __init__( + self, + model: Model, + mc_points: Tensor, + sampler: Optional[MCSampler] = None, + posterior_transform: Optional[PosteriorTransform] = None, + X_pending: Optional[Tensor] = None, + ) -> None: + r"""q-Integrated Negative Posterior Variance. + + Args: + model: A fitted model. + mc_points: A `batch_shape x N x d` tensor of points to use for + MC-integrating the posterior variance. Usually, these are qMC + samples on the whole design space, but biased sampling directly + allows weighted integration of the posterior variance. + sampler: The sampler used for drawing fantasy samples. In the basic setting + of a standard GP (default) this is a dummy, since the variance of the + model after conditioning does not actually depend on the sampled values. + posterior_transform: A PosteriorTransform. If using a multi-output model, + a PosteriorTransform that transforms the multi-output posterior into a + single-output posterior is required. + X_pending: A `n' x d`-dim Tensor of `n'` design points that have + points that have been submitted for function evaluation but + have not yet been evaluated. + """ + super().__init__(model=model) + self.posterior_transform = posterior_transform + if sampler is None: + # If no sampler is provided, we use the following dummy sampler for the + # fantasize() method in forward. IMPORTANT: This assumes that the posterior + # variance does not depend on the samples y (only on x), which is true for + # standard GP models, but not in general (e.g. for other likelihoods or + # heteroskedastic GPs using a separate noise model fit on data). + sampler = SobolQMCNormalSampler(sample_shape=torch.Size([1])) + self.sampler = sampler + self.X_pending = X_pending + self.register_buffer("mc_points", mc_points) + +
+[docs] + @concatenate_pending_points + @t_batch_mode_transform() + def forward(self, X: Tensor) -> Tensor: + # Construct the fantasy model (we actually do not use the full model, + # this is just a convenient way of computing fast posterior covariances + fantasy_model = self.model.fantasize( + X=X, + sampler=self.sampler, + ) + + bdims = tuple(1 for _ in X.shape[:-2]) + if self.model.num_outputs > 1: + # We use q=1 here b/c ScalarizedObjective currently does not fully exploit + # LinearOperator operations and thus may be slow / overly memory-hungry. + # TODO (T52818288): Properly use LinearOperators in scalarize_posterior + mc_points = self.mc_points.view(-1, *bdims, 1, X.size(-1)) + else: + # While we only need marginal variances, we can evaluate for q>1 + # b/c for GPyTorch models lazy evaluation can make this quite a bit + # faster than evaluating in t-batch mode with q-batch size of 1 + mc_points = self.mc_points.view(*bdims, -1, X.size(-1)) + + # evaluate the posterior at the grid points + with settings.propagate_grads(True): + posterior = fantasy_model.posterior( + mc_points, posterior_transform=self.posterior_transform + ) + + neg_variance = posterior.variance.mul(-1.0) + + if self.posterior_transform is None: + # if single-output, shape is 1 x batch_shape x num_grid_points x 1 + return neg_variance.mean(dim=-2).squeeze(-1).squeeze(0) + else: + # if multi-output + obj, shape is num_grid_points x batch_shape x 1 x 1 + return neg_variance.mean(dim=0).squeeze(-1).squeeze(-1)
+
+ + + +class PairwiseMCPosteriorVariance(MCAcquisitionFunction): + r"""Variance of difference for Active Learning + + Given a model and an objective, calculate the posterior sample variance + of the objective on the difference of pairs of points. See more implementation + details in `forward`. This acquisition function is typically used with a + pairwise model (e.g., PairwiseGP) and a likelihood/link function + on the pair difference (e.g., logistic or probit) for pure exploration + """ + + def __init__( + self, + model: Model, + objective: MCAcquisitionObjective, + sampler: Optional[MCSampler] = None, + ) -> None: + r"""Pairwise Monte Carlo Posterior Variance + + Args: + model: A fitted model. + objective: An MCAcquisitionObjective representing the link function + (e.g., logistic or probit.) applied on the difference of (usually 1-d) + two samples. Can be implemented via GenericMCObjective. + sampler: The sampler used for drawing MC samples. + """ + super().__init__( + model=model, sampler=sampler, objective=objective, X_pending=None + ) + + @t_batch_mode_transform() + def forward(self, X: Tensor) -> Tensor: + r"""Evaluate PairwiseMCPosteriorVariance on the candidate set `X`. + + Args: + X: A `batch_size x q x d`-dim Tensor. q should be a multiple of 2. + + Returns: + Tensor of shape `batch_size x q` representing the posterior variance + of link function at X that active learning hopes to maximize + """ + if X.shape[-2] == 0 or X.shape[-2] % 2 != 0: + raise RuntimeError( + "q must be a multiple of 2 for PairwiseMCPosteriorVariance" + ) + + # The output is of shape batch_shape x 2 x d + # For PairwiseGP, d = 1 + post = self.model.posterior(X) + samples = self.get_posterior_samples(post) # num_samples x batch_shape x 2 x d + + # The output is of shape num_samples x batch_shape x q/2 x d + # assuming the comparison is made between the 2 * i and 2 * i + 1 elements + samples_diff = samples[..., ::2, :] - samples[..., 1::2, :] + mc_var = self.objective(samples_diff).var(dim=0) + mean_mc_var = mc_var.mean(dim=-1) + + return mean_mc_var +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/botorch/acquisition/logei.html b/_modules/botorch/acquisition/logei.html new file mode 100644 index 0000000..6cfba58 --- /dev/null +++ b/_modules/botorch/acquisition/logei.html @@ -0,0 +1,1044 @@ + + + + + + + + + + botorch.acquisition.logei — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for botorch.acquisition.logei

+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# This source code is licensed under the MIT license found in the
+# LICENSE file in the root directory of this source tree.
+
+r"""
+Monte-Carlo variants of the LogEI family of improvements-based acquisition functions,
+see [Ament2023logei]_ for details.
+
+References
+
+.. [Ament2023logei]
+    S. Ament, S. Daulton, D. Eriksson, M. Balandat, and E. Bakshy.
+    Unexpected Improvements to Expected Improvement for Bayesian Optimization. Advances
+    in Neural Information Processing Systems 36, 2023.
+"""
+
+from __future__ import annotations
+
+from copy import deepcopy
+
+from functools import partial
+
+from typing import Callable, List, Optional, Tuple, TypeVar, Union
+
+import torch
+from botorch.acquisition.cached_cholesky import CachedCholeskyMCSamplerMixin
+from botorch.acquisition.monte_carlo import SampleReducingMCAcquisitionFunction
+from botorch.acquisition.objective import (
+    ConstrainedMCObjective,
+    MCAcquisitionObjective,
+    PosteriorTransform,
+)
+from botorch.acquisition.utils import (
+    compute_best_feasible_objective,
+    prune_inferior_points,
+)
+from botorch.exceptions.errors import BotorchError
+from botorch.models.model import Model
+from botorch.sampling.base import MCSampler
+from botorch.utils.safe_math import (
+    fatmax,
+    log_fatplus,
+    log_softplus,
+    logmeanexp,
+    smooth_amax,
+)
+from botorch.utils.transforms import match_batch_shape
+from torch import Tensor
+
+"""
+NOTE: On the default temperature parameters:
+
+tau_relu: It is generally important to set `tau_relu` to be very small, in particular,
+smaller than the expected improvement value. Otherwise, the optimization can stagnate.
+By setting `tau_relu=1e-6` by default, stagnation is exceedingly unlikely to occur due
+to the smooth ReLU approximation for practical applications of BO.
+IDEA: We could consider shrinking `tau_relu` with the progression of the optimization.
+
+tau_max: This is only relevant for the batch (`q > 1`) case, and `tau_max=1e-2` is
+sufficient to get a good approximation to the maximum improvement in the batch of
+candidates. If `fat=False`, the smooth approximation to the maximum can saturate
+numerically. It is therefore recommended to use `fat=True` when optimizing batches
+of `q > 1` points.
+"""
+TAU_RELU = 1e-6
+TAU_MAX = 1e-2
+FloatOrTensor = TypeVar("FloatOrTensor", float, Tensor)
+
+
+class LogImprovementMCAcquisitionFunction(SampleReducingMCAcquisitionFunction):
+    r"""
+    Abstract base class for Monte-Carlo-based batch LogEI acquisition functions.
+    """
+
+    _log: bool = True
+
+    def __init__(
+        self,
+        model: Model,
+        sampler: Optional[MCSampler] = None,
+        objective: Optional[MCAcquisitionObjective] = None,
+        posterior_transform: Optional[PosteriorTransform] = None,
+        X_pending: Optional[Tensor] = None,
+        constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
+        eta: Union[Tensor, float] = 1e-3,
+        fat: bool = True,
+        tau_max: float = TAU_MAX,
+    ) -> None:
+        r"""
+        Args:
+            model: A fitted model.
+            sampler: The sampler used to draw base samples. If not given,
+                a sampler is generated using `get_sampler`.
+                NOTE: For posteriors that do not support base samples,
+                a sampler compatible with intended use case must be provided.
+                See `ForkedRNGSampler` and `StochasticSampler` as examples.
+            objective: The MCAcquisitionObjective under which the samples are
+                evaluated. Defaults to `IdentityMCObjective()`.
+            posterior_transform: A PosteriorTransform (optional).
+            X_pending: A `batch_shape, m x d`-dim Tensor of `m` design points
+                that have points that have been submitted for function evaluation
+                but have not yet been evaluated.
+            constraints: A list of constraint callables which map a Tensor of posterior
+                samples of dimension `sample_shape x batch-shape x q x m`-dim to a
+                `sample_shape x batch-shape x q`-dim Tensor. The associated constraints
+                are satisfied if `constraint(samples) < 0`.
+            eta: Temperature parameter(s) governing the smoothness of the sigmoid
+                approximation to the constraint indicators. See the docs of
+                `compute_(log_)constraint_indicator` for more details on this parameter.
+            fat: Toggles the logarithmic / linear asymptotic behavior of the smooth
+                approximation to the ReLU.
+            tau_max: Temperature parameter controlling the sharpness of the
+                approximation to the `max` operator over the `q` candidate points.
+        """
+        if isinstance(objective, ConstrainedMCObjective):
+            raise BotorchError(
+                "Log-Improvement should not be used with `ConstrainedMCObjective`."
+                "Please pass the `constraints` directly to the constructor of the "
+                "acquisition function."
+            )
+        q_reduction = partial(fatmax if fat else smooth_amax, tau=tau_max)
+        super().__init__(
+            model=model,
+            sampler=sampler,
+            objective=objective,
+            posterior_transform=posterior_transform,
+            X_pending=X_pending,
+            sample_reduction=logmeanexp,
+            q_reduction=q_reduction,
+            constraints=constraints,
+            eta=eta,
+            fat=fat,
+        )
+        self.tau_max = tau_max
+
+
+
+[docs] +class qLogExpectedImprovement(LogImprovementMCAcquisitionFunction): + r"""MC-based batch Log Expected Improvement. + + This computes qLogEI by + (1) sampling the joint posterior over q points, + (2) evaluating the smoothed log improvement over the current best for each sample, + (3) smoothly maximizing over q, and + (4) averaging over the samples in log space. + + See [Ament2023logei]_ for details. Formally, + + `qLogEI(X) ~ log(qEI(X)) = log(E(max(max Y - best_f, 0)))`. + + where `Y ~ f(X)`, and `X = (x_1,...,x_q)`, . + + Example: + >>> model = SingleTaskGP(train_X, train_Y) + >>> best_f = train_Y.max()[0] + >>> sampler = SobolQMCNormalSampler(1024) + >>> qLogEI = qLogExpectedImprovement(model, best_f, sampler) + >>> qei = qLogEI(test_X) + """ + + def __init__( + self, + model: Model, + best_f: Union[float, Tensor], + sampler: Optional[MCSampler] = None, + objective: Optional[MCAcquisitionObjective] = None, + posterior_transform: Optional[PosteriorTransform] = None, + X_pending: Optional[Tensor] = None, + constraints: Optional[List[Callable[[Tensor], Tensor]]] = None, + eta: Union[Tensor, float] = 1e-3, + fat: bool = True, + tau_max: float = TAU_MAX, + tau_relu: float = TAU_RELU, + ) -> None: + r"""q-Log Expected Improvement. + + Args: + model: A fitted model. + best_f: The best objective value observed so far (assumed noiseless). Can be + a scalar, or a `batch_shape`-dim tensor. In case of a batched model, the + tensor can specify different values for each element of the batch. + sampler: The sampler used to draw base samples. See `MCAcquisitionFunction` + more details. + objective: The MCAcquisitionObjective under which the samples are evaluated. + Defaults to `IdentityMCObjective()`. + posterior_transform: A PosteriorTransform (optional). + X_pending: A `m x d`-dim Tensor of `m` design points that have been + submitted for function evaluation but have not yet been evaluated. + Concatenated into `X` upon forward call. Copied and set to have no + gradient. + constraints: A list of constraint callables which map a Tensor of posterior + samples of dimension `sample_shape x batch-shape x q x m`-dim to a + `sample_shape x batch-shape x q`-dim Tensor. The associated constraints + are satisfied if `constraint(samples) < 0`. + eta: Temperature parameter(s) governing the smoothness of the sigmoid + approximation to the constraint indicators. See the docs of + `compute_(log_)smoothed_constraint_indicator` for details. + fat: Toggles the logarithmic / linear asymptotic behavior of the smooth + approximation to the ReLU. + tau_max: Temperature parameter controlling the sharpness of the smooth + approximations to max. + tau_relu: Temperature parameter controlling the sharpness of the smooth + approximations to ReLU. + """ + super().__init__( + model=model, + sampler=sampler, + objective=objective, + posterior_transform=posterior_transform, + X_pending=X_pending, + constraints=constraints, + eta=eta, + tau_max=check_tau(tau_max, name="tau_max"), + fat=fat, + ) + self.register_buffer("best_f", torch.as_tensor(best_f, dtype=float)) + self.tau_relu = check_tau(tau_relu, name="tau_relu") + + def _sample_forward(self, obj: Tensor) -> Tensor: + r"""Evaluate qLogExpectedImprovement on the candidate set `X`. + + Args: + obj: `mc_shape x batch_shape x q`-dim Tensor of MC objective values. + + Returns: + A `mc_shape x batch_shape x q`-dim Tensor of expected improvement values. + """ + li = _log_improvement( + Y=obj, + best_f=self.best_f, + tau=self.tau_relu, + fat=self._fat, + ) + return li
+ + + +
+[docs] +class qLogNoisyExpectedImprovement( + LogImprovementMCAcquisitionFunction, CachedCholeskyMCSamplerMixin +): + r"""MC-based batch Log Noisy Expected Improvement. + + This function does not assume a `best_f` is known (which would require + noiseless observations). Instead, it uses samples from the joint posterior + over the `q` test points and previously observed points. A smooth approximation + to the canonical improvement over previously observed points is computed + for each sample and the logarithm of the average is returned. + + See [Ament2023logei]_ for details. Formally, + + `qLogNEI(X) ~ log(qNEI(X)) = Log E(max(max Y - max Y_baseline, 0))`, + + where `(Y, Y_baseline) ~ f((X, X_baseline)), X = (x_1,...,x_q)`. + + Example: + >>> model = SingleTaskGP(train_X, train_Y) + >>> sampler = SobolQMCNormalSampler(1024) + >>> qLogNEI = qLogNoisyExpectedImprovement(model, train_X, sampler) + >>> acqval = qLogNEI(test_X) + """ + + def __init__( + self, + model: Model, + X_baseline: Tensor, + sampler: Optional[MCSampler] = None, + objective: Optional[MCAcquisitionObjective] = None, + posterior_transform: Optional[PosteriorTransform] = None, + X_pending: Optional[Tensor] = None, + constraints: Optional[List[Callable[[Tensor], Tensor]]] = None, + eta: Union[Tensor, float] = 1e-3, + fat: bool = True, + prune_baseline: bool = False, + cache_root: bool = True, + tau_max: float = TAU_MAX, + tau_relu: float = TAU_RELU, + marginalize_dim: Optional[int] = None, + ) -> None: + r"""q-Noisy Expected Improvement. + + Args: + model: A fitted model. + X_baseline: A `batch_shape x r x d`-dim Tensor of `r` design points + that have already been observed. These points are considered as + the potential best design point. + sampler: The sampler used to draw base samples. See `MCAcquisitionFunction` + more details. + objective: The MCAcquisitionObjective under which the samples are + evaluated. Defaults to `IdentityMCObjective()`. + posterior_transform: A PosteriorTransform (optional). + X_pending: A `batch_shape x m x d`-dim Tensor of `m` design points + that have points that have been submitted for function evaluation + but have not yet been evaluated. Concatenated into `X` upon + forward call. Copied and set to have no gradient. + constraints: A list of constraint callables which map a Tensor of posterior + samples of dimension `sample_shape x batch-shape x q x m`-dim to a + `sample_shape x batch-shape x q`-dim Tensor. The associated constraints + are satisfied if `constraint(samples) < 0`. + eta: Temperature parameter(s) governing the smoothness of the sigmoid + approximation to the constraint indicators. See the docs of + `compute_(log_)smoothed_constraint_indicator` for details. + fat: Toggles the logarithmic / linear asymptotic behavior of the smooth + approximation to the ReLU. + prune_baseline: If True, remove points in `X_baseline` that are + highly unlikely to be the best point. This can significantly + improve performance and is generally recommended. In order to + customize pruning parameters, instead manually call + `botorch.acquisition.utils.prune_inferior_points` on `X_baseline` + before instantiating the acquisition function. + cache_root: A boolean indicating whether to cache the root + decomposition over `X_baseline` and use low-rank updates. + tau_max: Temperature parameter controlling the sharpness of the smooth + approximations to max. + tau_relu: Temperature parameter controlling the sharpness of the smooth + approximations to ReLU. + marginalize_dim: The dimension to marginalize over. + + TODO: similar to qNEHVI, when we are using sequential greedy candidate + selection, we could incorporate pending points X_baseline and compute + the incremental q(Log)NEI from the new point. This would greatly increase + efficiency for large batches. + """ + # TODO: separate out baseline variables initialization and other functions + # in qNEI to avoid duplication of both code and work at runtime. + super().__init__( + model=model, + sampler=sampler, + objective=objective, + posterior_transform=posterior_transform, + X_pending=X_pending, + constraints=constraints, + eta=eta, + fat=fat, + tau_max=tau_max, + ) + self.tau_relu = tau_relu + self._init_baseline( + model=model, + X_baseline=X_baseline, + sampler=sampler, + objective=objective, + posterior_transform=posterior_transform, + prune_baseline=prune_baseline, + cache_root=cache_root, + marginalize_dim=marginalize_dim, + ) + + def _sample_forward(self, obj: Tensor) -> Tensor: + r"""Evaluate qLogNoisyExpectedImprovement per sample on the candidate set `X`. + + Args: + obj: `mc_shape x batch_shape x q`-dim Tensor of MC objective values. + + Returns: + A `sample_shape x batch_shape x q`-dim Tensor of log noisy expected smoothed + improvement values. + """ + return _log_improvement( + Y=obj, + best_f=self.compute_best_f(obj), + tau=self.tau_relu, + fat=self._fat, + ) + + def _init_baseline( + self, + model: Model, + X_baseline: Tensor, + sampler: Optional[MCSampler] = None, + objective: Optional[MCAcquisitionObjective] = None, + posterior_transform: Optional[PosteriorTransform] = None, + prune_baseline: bool = False, + cache_root: bool = True, + marginalize_dim: Optional[int] = None, + ) -> None: + CachedCholeskyMCSamplerMixin.__init__( + self, model=model, cache_root=cache_root, sampler=sampler + ) + if prune_baseline: + X_baseline = prune_inferior_points( + model=model, + X=X_baseline, + objective=objective, + posterior_transform=posterior_transform, + marginalize_dim=marginalize_dim, + constraints=self._constraints, + ) + self.register_buffer("X_baseline", X_baseline) + # registering buffers for _get_samples_and_objectives in the next `if` block + self.register_buffer("baseline_samples", None) + self.register_buffer("baseline_obj", None) + if self._cache_root: + self.q_in = -1 + # set baseline samples + with torch.no_grad(): # this is _get_samples_and_objectives(X_baseline) + posterior = self.model.posterior( + X_baseline, posterior_transform=self.posterior_transform + ) + # Note: The root decomposition is cached in two different places. It + # may be confusing to have two different caches, but this is not + # trivial to change since each is needed for a different reason: + # - LinearOperator caching to `posterior.mvn` allows for reuse within + # this function, which may be helpful if the same root decomposition + # is produced by the calls to `self.base_sampler` and + # `self._cache_root_decomposition`. + # - self._baseline_L allows a root decomposition to be persisted outside + # this method. + self.baseline_samples = self.get_posterior_samples(posterior) + self.baseline_obj = self.objective(self.baseline_samples, X=X_baseline) + + # We make a copy here because we will write an attribute `base_samples` + # to `self.base_sampler.base_samples`, and we don't want to mutate + # `self.sampler`. + self.base_sampler = deepcopy(self.sampler) + self.register_buffer( + "_baseline_best_f", + self._compute_best_feasible_objective( + samples=self.baseline_samples, obj=self.baseline_obj + ), + ) + self._baseline_L = self._compute_root_decomposition(posterior=posterior) + +
+[docs] + def compute_best_f(self, obj: Tensor) -> Tensor: + """Computes the best (feasible) noisy objective value. + + Args: + obj: `sample_shape x batch_shape x q`-dim Tensor of objectives in forward. + + Returns: + A `sample_shape x batch_shape`-dim Tensor of best feasible objectives. + """ + if self._cache_root: + val = self._baseline_best_f + else: + val = self._compute_best_feasible_objective( + samples=self.baseline_samples, obj=self.baseline_obj + ) + # ensuring shape, dtype, device compatibility with obj + n_sample_dims = len(self.sample_shape) + view_shape = torch.Size( + [ + *val.shape[:n_sample_dims], # sample dimensions + *(1,) * (obj.ndim - val.ndim - 1), # pad to match obj without `q`-dim + *val.shape[n_sample_dims:], # the rest + ] + ) + return val.view(view_shape).to(obj) # obj.shape[:-1], i.e. without `q`-dim`
+ + + def _get_samples_and_objectives(self, X: Tensor) -> Tuple[Tensor, Tensor]: + r"""Compute samples at new points, using the cached root decomposition. + + Args: + X: A `batch_shape x q x d`-dim tensor of inputs. + + Returns: + A two-tuple `(samples, obj)`, where `samples` is a tensor of posterior + samples with shape `sample_shape x batch_shape x q x m`, and `obj` is a + tensor of MC objective values with shape `sample_shape x batch_shape x q`. + """ + n_baseline, q = self.X_baseline.shape[-2], X.shape[-2] + X_full = torch.cat([match_batch_shape(self.X_baseline, X), X], dim=-2) + # TODO: Implement more efficient way to compute posterior over both training and + # test points in GPyTorch (https://github.com/cornellius-gp/gpytorch/issues/567) + posterior = self.model.posterior( + X_full, posterior_transform=self.posterior_transform + ) + if not self._cache_root: + samples_full = super().get_posterior_samples(posterior) + obj_full = self.objective(samples_full, X=X_full) + # assigning baseline buffers so `best_f` can be computed in _sample_forward + self.baseline_samples, samples = samples_full.split([n_baseline, q], dim=-2) + self.baseline_obj, obj = obj_full.split([n_baseline, q], dim=-1) + return samples, obj + + # handle one-to-many input transforms + n_plus_q = X_full.shape[-2] + n_w = posterior._extended_shape()[-2] // n_plus_q + q_in = q * n_w + self._set_sampler(q_in=q_in, posterior=posterior) + samples = self._get_f_X_samples(posterior=posterior, q_in=q_in) + obj = self.objective(samples, X=X_full[..., -q:, :]) + return samples, obj + + def _compute_best_feasible_objective(self, samples: Tensor, obj: Tensor) -> Tensor: + r"""Computes best feasible objective value from samples. + + Args: + samples: `sample_shape x batch_shape x q x m`-dim posterior samples. + obj: A `sample_shape x batch_shape x q`-dim Tensor of MC objective values. + + Returns: + A `sample_shape x batch_shape`-dim Tensor of best feasible objectives. + """ + return compute_best_feasible_objective( + samples=samples, + obj=obj, + constraints=self._constraints, + model=self.model, + objective=self.objective, + posterior_transform=self.posterior_transform, + X_baseline=self.X_baseline, + )
+ + + +""" +###################################### utils ########################################## +""" + + +def _log_improvement( + Y: Tensor, + best_f: Tensor, + tau: Union[float, Tensor], + fat: bool, +) -> Tensor: + """Computes the logarithm of the softplus-smoothed improvement, i.e. + `log_softplus(Y - best_f, beta=(1 / tau))`. + Note that softplus is an approximation to the regular ReLU objective whose maximum + pointwise approximation error is linear with respect to tau as tau goes to zero. + + Args: + obj: `mc_samples x batch_shape x q`-dim Tensor of output samples. + best_f: Best previously observed objective value(s), broadcastable with + `mc_samples x batch_shape`-dim Tensor, i.e. `obj`'s dims without `q`. + tau: Temperature parameter for smooth approximation of ReLU. + as `tau -> 0`, maximum pointwise approximation error is linear w.r.t. `tau`. + fat: Toggles the logarithmic / linear asymptotic behavior of the + smooth approximation to ReLU. + + Returns: + A `mc_samples x batch_shape x q`-dim Tensor of improvement values. + """ + log_soft_clamp = log_fatplus if fat else log_softplus + Z = Y - best_f.unsqueeze(-1).to(Y) + return log_soft_clamp(Z, tau=tau) # ~ ((Y - best_f) / Y_std).clamp(0) + + +def check_tau(tau: FloatOrTensor, name: str) -> FloatOrTensor: + """Checks the validity of the tau arguments of the functions below, and returns + `tau` if it is valid.""" + if isinstance(tau, Tensor) and tau.numel() != 1: + raise ValueError(name + f" is not a scalar: {tau.numel() = }.") + if not (tau > 0): + raise ValueError(name + f" is non-positive: {tau = }.") + return tau +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/botorch/acquisition/monte_carlo.html b/_modules/botorch/acquisition/monte_carlo.html new file mode 100644 index 0000000..d0a5d75 --- /dev/null +++ b/_modules/botorch/acquisition/monte_carlo.html @@ -0,0 +1,1371 @@ + + + + + + + + + + botorch.acquisition.monte_carlo — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for botorch.acquisition.monte_carlo

+#!/usr/bin/env python3
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# This source code is licensed under the MIT license found in the
+# LICENSE file in the root directory of this source tree.
+
+r"""
+Batch acquisition functions using the reparameterization trick in combination
+with (quasi) Monte-Carlo sampling. See [Rezende2014reparam]_, [Wilson2017reparam]_ and
+[Balandat2020botorch]_.
+
+References
+
+.. [Rezende2014reparam]
+    D. J. Rezende, S. Mohamed, and D. Wierstra. Stochastic backpropagation and
+    approximate inference in deep generative models. ICML 2014.
+
+.. [Wilson2017reparam]
+    J. T. Wilson, R. Moriconi, F. Hutter, and M. P. Deisenroth.
+    The reparameterization trick for acquisition functions. ArXiv 2017.
+"""
+
+from __future__ import annotations
+
+import math
+from abc import ABC, abstractmethod
+from copy import deepcopy
+from functools import partial
+from typing import Callable, List, Optional, Protocol, Tuple, Union
+
+import torch
+from botorch.acquisition.acquisition import AcquisitionFunction, MCSamplerMixin
+from botorch.acquisition.cached_cholesky import CachedCholeskyMCSamplerMixin
+from botorch.acquisition.objective import (
+    ConstrainedMCObjective,
+    IdentityMCObjective,
+    MCAcquisitionObjective,
+    PosteriorTransform,
+)
+from botorch.acquisition.utils import (
+    compute_best_feasible_objective,
+    prune_inferior_points,
+    repeat_to_match_aug_dim,
+)
+from botorch.exceptions.errors import UnsupportedError
+from botorch.exceptions.warnings import legacy_ei_numerics_warning
+from botorch.models.model import Model
+from botorch.sampling.base import MCSampler
+from botorch.utils.objective import compute_smoothed_feasibility_indicator
+from botorch.utils.transforms import (
+    concatenate_pending_points,
+    match_batch_shape,
+    t_batch_mode_transform,
+)
+from torch import Tensor
+
+
+class MCAcquisitionFunction(AcquisitionFunction, MCSamplerMixin, ABC):
+    r"""
+    Abstract base class for Monte-Carlo based batch acquisition functions.
+    """
+
+    def __init__(
+        self,
+        model: Model,
+        sampler: Optional[MCSampler] = None,
+        objective: Optional[MCAcquisitionObjective] = None,
+        posterior_transform: Optional[PosteriorTransform] = None,
+        X_pending: Optional[Tensor] = None,
+    ) -> None:
+        r"""
+        Args:
+            model: A fitted model.
+            sampler: The sampler used to draw base samples. If not given,
+                a sampler is generated on the fly within the
+                `get_posterior_samples` method using
+                `botorch.sampling.get_sampler`.
+                NOTE: For posteriors that do not support base samples,
+                a sampler compatible with intended use case must be provided.
+                See `ForkedRNGSampler` and `StochasticSampler` as examples.
+            objective: The MCAcquisitionObjective under which the samples are
+                evaluated. Defaults to `IdentityMCObjective()`.
+            posterior_transform: A PosteriorTransform (optional).
+            X_pending: A `batch_shape, m x d`-dim Tensor of `m` design points
+                that have points that have been submitted for function evaluation
+                but have not yet been evaluated.
+        """
+        super().__init__(model=model)
+        MCSamplerMixin.__init__(self, sampler=sampler)
+        if objective is None and model.num_outputs != 1:
+            if posterior_transform is None:
+                raise UnsupportedError(
+                    "Must specify an objective or a posterior transform when using "
+                    "a multi-output model."
+                )
+            elif not posterior_transform.scalarize:
+                raise UnsupportedError(
+                    "If using a multi-output model without an objective, "
+                    "posterior_transform must scalarize the output."
+                )
+        if objective is None:
+            objective = IdentityMCObjective()
+        self.posterior_transform = posterior_transform
+        self.objective: MCAcquisitionObjective = objective
+        self.set_X_pending(X_pending)
+
+    def _get_samples_and_objectives(self, X: Tensor) -> Tuple[Tensor, Tensor]:
+        """Computes posterior samples and objective values at input X.
+
+        Args:
+            X: A `batch_shape x q x d`-dim Tensor of model inputs.
+
+        Returns:
+            A two-tuple `(samples, obj)`, where `samples` is a tensor of posterior
+            samples with shape `sample_shape x batch_shape x q x m`, and `obj` is a
+            tensor of MC objective values with shape `sample_shape x batch_shape x q`.
+        """
+        posterior = self.model.posterior(
+            X=X, posterior_transform=self.posterior_transform
+        )
+        samples = self.get_posterior_samples(posterior)
+        obj = self.objective(samples=samples, X=X)
+        return samples, obj
+
+    @abstractmethod
+    def forward(self, X: Tensor) -> Tensor:
+        r"""Takes in a `batch_shape x q x d` X Tensor of t-batches with `q` `d`-dim
+        design points each, and returns a Tensor with shape `batch_shape'`, where
+        `batch_shape'` is the broadcasted batch shape of model and input `X`. Should
+        utilize the result of `set_X_pending` as needed to account for pending function
+        evaluations.
+        """
+        pass  # pragma: no cover
+
+
+class SampleReductionProtocol(Protocol):
+    """For static type check of SampleReducingMCAcquisitionFunction's mc_reduction."""
+
+    @staticmethod
+    def __call__(X: Tensor, *, dim: torch.Size) -> Tensor:
+        pass  # pragma: no cover
+
+
+class SampleReducingMCAcquisitionFunction(MCAcquisitionFunction):
+    r"""MC-based batch acquisition function that reduces across samples and implements
+    a general treatment of outcome constraints.
+
+    This class's `forward` computes the - possibly constrained - acquisition value by
+    (1) computing the unconstrained utility for each MC sample using `_sample_forward`,
+    (2) weighing the utility values by the constraint indicator per MC sample, and
+    (3) reducing (e.g. averaging) the weighted utility values over the MC dimension.
+
+    NOTE: Do *NOT* override the `forward` method, unless you have thought about it well.
+
+    `forward` is implemented generically to incorporate constraints in a principled way,
+    and takes care of reducing over the Monte Carlo and batch dimensions via the
+    `sample_reduction` and `q_reduction` arguments, which default to `torch.mean` and
+    `torch.max`, respectively.
+
+    In order to implement a custom SampleReducingMCAcquisitionFunction, we only need to
+    implement the `_sample_forward(obj: Tensor) -> Tensor` method, which maps objective
+    samples to acquisition utility values without reducing the Monte Carlo and batch
+    (i.e. q) dimensions (see details in the docstring of `_sample_forward`).
+
+    A note on design choices:
+
+    The primary purpose of `SampleReducingMCAcquisitionFunction`is to support outcome
+    constraints. On the surface, designing a wrapper `ConstrainedMCAcquisitionFunction`
+    could be an elegant solution to this end, but it would still require the acquisition
+    functions to implement a `_sample_forward` method to weigh acquisition utilities at
+    the sample level. Further, `qNoisyExpectedImprovement` is a special case that is
+    hard to encompass in this pattern, since it requires the computation of the best
+    *feasible* objective, which requires access to the constraint functions. However,
+    if the constraints are stored in a wrapper class, they will be inaccessible to the
+    forward pass. These problems are circumvented by the design of this class.
+    """
+
+    _log: bool = False  # whether the acquisition utilities are in log-space
+
+    def __init__(
+        self,
+        model: Model,
+        sampler: Optional[MCSampler] = None,
+        objective: Optional[MCAcquisitionObjective] = None,
+        posterior_transform: Optional[PosteriorTransform] = None,
+        X_pending: Optional[Tensor] = None,
+        sample_reduction: SampleReductionProtocol = torch.mean,
+        q_reduction: SampleReductionProtocol = torch.amax,
+        constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
+        eta: Union[Tensor, float] = 1e-3,
+        fat: bool = False,
+    ):
+        r"""Constructor of SampleReducingMCAcquisitionFunction.
+
+        Args:
+            model: A fitted model.
+            sampler: The sampler used to draw base samples. If not given, a
+                sampler is generated on the fly within the
+                `get_posterior_samples` method using
+                `botorch.sampling.get_sampler`.
+                NOTE: For posteriors that do not support base samples,
+                a sampler compatible with intended use case must be provided.
+                See `ForkedRNGSampler` and `StochasticSampler` as examples.
+            objective: The MCAcquisitionObjective under which the samples are
+                evaluated. Defaults to `IdentityMCObjective()`.
+                NOTE: `ConstrainedMCObjective` for outcome constraints is deprecated in
+                favor of passing the `constraints` directly to this constructor.
+            posterior_transform: A `PosteriorTransform` (optional).
+            X_pending: A `batch_shape, m x d`-dim Tensor of `m` design points
+                that have points that have been submitted for function evaluation
+                but have not yet been evaluated.
+            sample_reduction: A callable that takes in a `sample_shape x batch_shape`
+                Tensor of acquisition utility values, a keyword-argument `dim` that
+                specifies the sample dimensions to reduce over, and returns a
+                `batch_shape`-dim Tensor of acquisition values.
+            q_reduction: A callable that takes in a `sample_shape x batch_shape x q`
+                Tensor of acquisition utility values, a keyword-argument `dim` that
+                specifies the q dimension to reduce over (i.e. -1), and returns a
+                `sample_shape x batch_shape`-dim Tensor of acquisition values.
+            constraints: A list of constraint callables which map a Tensor of posterior
+                samples of dimension `sample_shape x batch-shape x q x m`-dim to a
+                `sample_shape x batch-shape x q`-dim Tensor. The associated constraints
+                are considered satisfied if the output is less than zero.
+                NOTE: Constraint-weighting is only compatible with non-negative
+                acquistion utilities, e.g. all improvement-based acquisition functions.
+            eta: Temperature parameter(s) governing the smoothness of the sigmoid
+                approximation to the constraint indicators. For more details, on this
+                parameter, see the docs of `compute_smoothed_feasibility_indicator`.
+            fat: Wether to apply a fat-tailed smooth approximation to the feasibility
+                indicator or the canonical sigmoid approximation.
+        """
+        if constraints is not None and isinstance(objective, ConstrainedMCObjective):
+            raise ValueError(
+                "ConstrainedMCObjective as well as constraints passed to constructor."
+                "Choose one or the other, preferably the latter."
+            )
+        # TODO: deprecate ConstrainedMCObjective
+        super().__init__(
+            model=model,
+            sampler=sampler,
+            objective=objective,
+            posterior_transform=posterior_transform,
+            X_pending=X_pending,
+        )
+        # Shall the need arise, sample_dim could be exposed in the constructor.
+        sample_dim = tuple(range(len(self.sample_shape)))
+        self._sample_reduction = partial(sample_reduction, dim=sample_dim)
+        self._q_reduction = partial(q_reduction, dim=-1)
+        self._constraints = constraints
+        self._eta = eta
+        self._fat = fat
+
+    @concatenate_pending_points
+    @t_batch_mode_transform()
+    def forward(self, X: Tensor) -> Tensor:
+        r"""Computes the acquisition value associated with the input `X`. Weighs the
+        acquisition utility values by smoothed constraint indicators if `constraints`
+        was passed to the constructor of the class. Applies `self.sample_reduction` and
+        `self.q_reduction` to reduce over the Monte Carlo and batch (q) dimensions.
+
+        NOTE: Do *NOT* override the `forward` method for a custom acquisition function.
+        Instead, implement the `_sample_forward` method. See the docstring of this class
+        for details.
+
+        Args:
+            X: A `batch_shape x q x d` Tensor of t-batches with `q` `d`-dim
+                design points each.
+
+        Returns:
+            A Tensor with shape `batch_shape'`, where `batch_shape'` is the broadcasted
+            batch shape of model and input `X`.
+        """
+        non_reduced_acqval = self._non_reduced_forward(X=X)
+        return self._sample_reduction(self._q_reduction(non_reduced_acqval))
+
+    def _non_reduced_forward(self, X: Tensor) -> Tensor:
+        """Compute the constrained acquisition values at the MC-sample, q level.
+
+        Args:
+            X: A `batch_shape x q x d` Tensor of t-batches with `q` `d`-dim
+                design points each.
+
+        Returns:
+            A Tensor with shape `sample_sample x batch_shape x q`.
+        """
+        samples, obj = self._get_samples_and_objectives(X)
+        samples = repeat_to_match_aug_dim(target_tensor=samples, reference_tensor=obj)
+        acqval = self._sample_forward(obj)  # `sample_sample x batch_shape x q`
+        return self._apply_constraints(acqval=acqval, samples=samples)
+
+    @abstractmethod
+    def _sample_forward(self, obj: Tensor) -> Tensor:
+        """Evaluates the acquisition utility per MC sample based on objective value obj.
+        Should utilize the result of `set_X_pending` as needed to account for pending
+        function evaluations.
+
+        Args:
+            obj: A `sample_shape x batch_shape x q`-dim Tensor of MC objective values.
+
+        Returns:
+            A `sample_shape x batch_shape x q`-dim Tensor of acquisition utility values.
+        """
+        pass  # pragma: no cover
+
+    def _apply_constraints(self, acqval: Tensor, samples: Tensor) -> Tensor:
+        """Multiplies the acquisition utility by constraint indicators.
+
+        Args:
+            acqval: `sample_shape x batch_shape x q`-dim acquisition utility values.
+            samples: `sample_shape x batch_shape x q x m`-dim posterior samples.
+
+        Returns:
+            A `sample_shape x batch_shape x q`-dim Tensor of acquisition utility values
+                multiplied by a smoothed constraint indicator per sample.
+        """
+        if self._constraints is not None:
+            if not self._log and (acqval < 0).any():
+                raise ValueError(
+                    "Constraint-weighting requires unconstrained "
+                    "acquisition values to be non-negative."
+                )
+            ind = compute_smoothed_feasibility_indicator(
+                constraints=self._constraints,
+                samples=samples,
+                eta=self._eta,
+                log=self._log,
+                fat=self._fat,
+            )
+            acqval = acqval.add(ind) if self._log else acqval.mul(ind)
+        return acqval
+
+
+class qExpectedImprovement(SampleReducingMCAcquisitionFunction):
+    r"""MC-based batch Expected Improvement.
+
+    This computes qEI by
+    (1) sampling the joint posterior over q points
+    (2) evaluating the improvement over the current best for each sample
+    (3) maximizing over q
+    (4) averaging over the samples
+
+    `qEI(X) = E(max(max Y - best_f, 0)), Y ~ f(X), where X = (x_1,...,x_q)`
+
+    Example:
+        >>> model = SingleTaskGP(train_X, train_Y)
+        >>> best_f = train_Y.max()[0]
+        >>> sampler = SobolQMCNormalSampler(1024)
+        >>> qEI = qExpectedImprovement(model, best_f, sampler)
+        >>> qei = qEI(test_X)
+
+    NOTE: It is strongly recommended to use qLogExpectedImprovement instead
+    of regular qEI, as it can lead to substantially improved BO performance through
+    improved numerics. See https://arxiv.org/abs/2310.20708 for details.
+    """
+
+    def __init__(
+        self,
+        model: Model,
+        best_f: Union[float, Tensor],
+        sampler: Optional[MCSampler] = None,
+        objective: Optional[MCAcquisitionObjective] = None,
+        posterior_transform: Optional[PosteriorTransform] = None,
+        X_pending: Optional[Tensor] = None,
+        constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
+        eta: Union[Tensor, float] = 1e-3,
+    ) -> None:
+        r"""q-Expected Improvement.
+
+        Args:
+            model: A fitted model.
+            best_f: The best objective value observed so far (assumed noiseless). Can be
+                a scalar, or a `batch_shape`-dim tensor. In case of a batched model, the
+                tensor can specify different values for each element of the batch.
+            sampler: The sampler used to draw base samples. See `MCAcquisitionFunction`
+                more details.
+            objective: The MCAcquisitionObjective under which the samples are evaluated.
+                Defaults to `IdentityMCObjective()`.
+                NOTE: `ConstrainedMCObjective` for outcome constraints is deprecated in
+                favor of passing the `constraints` directly to this constructor.
+            posterior_transform: A PosteriorTransform (optional).
+            X_pending:  A `m x d`-dim Tensor of `m` design points that have been
+                submitted for function evaluation but have not yet been evaluated.
+                Concatenated into X upon forward call. Copied and set to have no
+                gradient.
+            constraints: A list of constraint callables which map a Tensor of posterior
+                samples of dimension `sample_shape x batch-shape x q x m`-dim to a
+                `sample_shape x batch-shape x q`-dim Tensor. The associated constraints
+                are considered satisfied if the output is less than zero.
+            eta: Temperature parameter(s) governing the smoothness of the sigmoid
+                approximation to the constraint indicators. For more details, on this
+                parameter, see the docs of `compute_smoothed_feasibility_indicator`.
+        """
+        legacy_ei_numerics_warning(legacy_name=type(self).__name__)
+        super().__init__(
+            model=model,
+            sampler=sampler,
+            objective=objective,
+            posterior_transform=posterior_transform,
+            X_pending=X_pending,
+            constraints=constraints,
+            eta=eta,
+        )
+        self.register_buffer("best_f", torch.as_tensor(best_f, dtype=float))
+
+    def _sample_forward(self, obj: Tensor) -> Tensor:
+        r"""Evaluate qExpectedImprovement per sample on the candidate set `X`.
+
+        Args:
+            obj: A `sample_shape x batch_shape x q`-dim Tensor of MC objective values.
+
+        Returns:
+            A `sample_shape x batch_shape x q`-dim Tensor of improvement utility values.
+        """
+        return (obj - self.best_f.unsqueeze(-1).to(obj)).clamp_min(0)
+
+
+class qNoisyExpectedImprovement(
+    SampleReducingMCAcquisitionFunction, CachedCholeskyMCSamplerMixin
+):
+    r"""MC-based batch Noisy Expected Improvement.
+
+    This function does not assume a `best_f` is known (which would require
+    noiseless observations). Instead, it uses samples from the joint posterior
+    over the `q` test points and previously observed points. The improvement
+    over previously observed points is computed for each sample and averaged.
+
+    `qNEI(X) = E(max(max Y - max Y_baseline, 0))`, where
+    `(Y, Y_baseline) ~ f((X, X_baseline)), X = (x_1,...,x_q)`
+
+    Example:
+        >>> model = SingleTaskGP(train_X, train_Y)
+        >>> sampler = SobolQMCNormalSampler(1024)
+        >>> qNEI = qNoisyExpectedImprovement(model, train_X, sampler)
+        >>> qnei = qNEI(test_X)
+
+    NOTE: It is strongly recommended to use qLogNoisyExpectedImprovement instead
+    of regular qNEI, as it can lead to substantially improved BO performance through
+    improved numerics. See https://arxiv.org/abs/2310.20708 for details.
+    """
+
+    def __init__(
+        self,
+        model: Model,
+        X_baseline: Tensor,
+        sampler: Optional[MCSampler] = None,
+        objective: Optional[MCAcquisitionObjective] = None,
+        posterior_transform: Optional[PosteriorTransform] = None,
+        X_pending: Optional[Tensor] = None,
+        prune_baseline: bool = True,
+        cache_root: bool = True,
+        constraints: Optional[List[Callable[[Tensor], Tensor]]] = None,
+        eta: Union[Tensor, float] = 1e-3,
+        marginalize_dim: Optional[int] = None,
+    ) -> None:
+        r"""q-Noisy Expected Improvement.
+
+        Args:
+            model: A fitted model.
+            X_baseline: A `batch_shape x r x d`-dim Tensor of `r` design points
+                that have already been observed. These points are considered as
+                the potential best design point.
+            sampler: The sampler used to draw base samples. See `MCAcquisitionFunction`
+                more details.
+            objective: The MCAcquisitionObjective under which the samples are
+                evaluated. Defaults to `IdentityMCObjective()`.
+                NOTE: `ConstrainedMCObjective` for outcome constraints is deprecated in
+                favor of passing the `constraints` directly to this constructor.
+            posterior_transform: A PosteriorTransform (optional).
+            X_pending: A `batch_shape x m x d`-dim Tensor of `m` design points
+                that have points that have been submitted for function evaluation
+                but have not yet been evaluated. Concatenated into `X` upon
+                forward call. Copied and set to have no gradient.
+            prune_baseline: If True, remove points in `X_baseline` that are
+                highly unlikely to be the best point. This can significantly
+                improve performance and is generally recommended. In order to
+                customize pruning parameters, instead manually call
+                `botorch.acquisition.utils.prune_inferior_points` on `X_baseline`
+                before instantiating the acquisition function.
+            cache_root: A boolean indicating whether to cache the root
+                decomposition over `X_baseline` and use low-rank updates.
+            constraints: A list of constraint callables which map a Tensor of posterior
+                samples of dimension `sample_shape x batch-shape x q x m`-dim to a
+                `sample_shape x batch-shape x q`-dim Tensor. The associated constraints
+                are considered satisfied if the output is less than zero.
+            eta: Temperature parameter(s) governing the smoothness of the sigmoid
+                approximation to the constraint indicators. For more details, on this
+                parameter, see the docs of `compute_smoothed_feasibility_indicator`.
+            marginalize_dim: The dimension to marginalize over.
+
+        TODO: similar to qNEHVI, when we are using sequential greedy candidate
+        selection, we could incorporate pending points X_baseline and compute
+        the incremental qNEI from the new point. This would greatly increase
+        efficiency for large batches.
+        """
+        legacy_ei_numerics_warning(legacy_name=type(self).__name__)
+        super().__init__(
+            model=model,
+            sampler=sampler,
+            objective=objective,
+            posterior_transform=posterior_transform,
+            X_pending=X_pending,
+            constraints=constraints,
+            eta=eta,
+        )
+        CachedCholeskyMCSamplerMixin.__init__(
+            self, model=model, cache_root=cache_root, sampler=sampler
+        )
+        if prune_baseline:
+            X_baseline = prune_inferior_points(
+                model=model,
+                X=X_baseline,
+                objective=objective,
+                posterior_transform=posterior_transform,
+                constraints=self._constraints,
+                marginalize_dim=marginalize_dim,
+            )
+        self.register_buffer("X_baseline", X_baseline)
+        # registering buffers for _get_samples_and_objectives in the next `if` block
+        self.register_buffer("baseline_samples", None)
+        self.register_buffer("baseline_obj", None)
+        if self._cache_root:
+            self.q_in = -1
+            # set baseline samples
+            with torch.no_grad():  # this is _get_samples_and_objectives(X_baseline)
+                posterior = self.model.posterior(
+                    X_baseline, posterior_transform=self.posterior_transform
+                )
+                # Note: The root decomposition is cached in two different places. It
+                # may be confusing to have two different caches, but this is not
+                # trivial to change since each is needed for a different reason:
+                # - LinearOperator caching to `posterior.mvn` allows for reuse within
+                #  this function, which may be helpful if the same root decomposition
+                #  is produced by the calls to `self.base_sampler` and
+                #  `self._cache_root_decomposition`.
+                # - self._baseline_L allows a root decomposition to be persisted outside
+                #   this method.
+                baseline_samples = self.get_posterior_samples(posterior)
+                baseline_obj = self.objective(baseline_samples, X=X_baseline)
+
+            # We make a copy here because we will write an attribute `base_samples`
+            # to `self.base_sampler.base_samples`, and we don't want to mutate
+            # `self.sampler`.
+            self.base_sampler = deepcopy(self.sampler)
+            self.baseline_samples = baseline_samples
+            self.baseline_obj = baseline_obj
+            self.register_buffer(
+                "_baseline_best_f",
+                self._compute_best_feasible_objective(
+                    samples=baseline_samples, obj=baseline_obj
+                ),  # `sample_shape x batch_shape`-dim
+            )
+            self._baseline_L = self._compute_root_decomposition(posterior=posterior)
+
+    def compute_best_f(self, obj: Tensor) -> Tensor:
+        """Computes the best (feasible) noisy objective value.
+
+        Args:
+            obj: `sample_shape x batch_shape x q`-dim Tensor of objectives in forward.
+
+        Returns:
+            A `sample_shape x batch_shape`-dim Tensor of best feasible objectives.
+        """
+        if self._cache_root:
+            val = self._baseline_best_f
+        else:
+            val = self._compute_best_feasible_objective(
+                samples=self.baseline_samples, obj=self.baseline_obj
+            )
+        # ensuring shape, dtype, device compatibility with obj
+        n_sample_dims = len(self.sample_shape)
+        view_shape = torch.Size(
+            [
+                *val.shape[:n_sample_dims],  # sample dimensions
+                *(1,) * (obj.ndim - val.ndim - 1),  # pad to match obj, without `q`-dim
+                *val.shape[n_sample_dims:],  # the rest
+            ]
+        )
+        return val.view(view_shape).to(obj)  # obj.shape[:-1], i.e. without `q`-dim`
+
+    def _sample_forward(self, obj: Tensor) -> Tensor:
+        """Evaluate qNoisyExpectedImprovement per objective value in `obj`.
+
+        Args:
+            obj: A `sample_shape x batch_shape x q`-dim Tensor of MC objective values.
+
+        Returns:
+            A `sample_shape x batch_shape x q`-dim Tensor of noisy improvement values.
+        """
+        return (obj - self.compute_best_f(obj).unsqueeze(-1)).clamp_min(0)
+
+    def _get_samples_and_objectives(self, X: Tensor) -> Tuple[Tensor, Tensor]:
+        r"""Compute samples at new points, using the cached root decomposition.
+
+        Args:
+            X: A `batch_shape x q x d`-dim tensor of inputs.
+
+        Returns:
+            A two-tuple `(samples, obj)`, where `samples` is a tensor of posterior
+            samples with shape `sample_shape x batch_shape x q x m`, and `obj` is a
+            tensor of MC objective values with shape `sample_shape x batch_shape x q`.
+        """
+        q = X.shape[-2]
+        X_full = torch.cat([match_batch_shape(self.X_baseline, X), X], dim=-2)
+        # TODO: Implement more efficient way to compute posterior over both training and
+        # test points in GPyTorch (https://github.com/cornellius-gp/gpytorch/issues/567)
+        posterior = self.model.posterior(
+            X_full, posterior_transform=self.posterior_transform
+        )
+        if not self._cache_root:
+            samples_full = super().get_posterior_samples(posterior)
+            samples = samples_full[..., -q:, :]
+            obj_full = self.objective(samples_full, X=X_full)
+            # assigning baseline buffers so `best_f` can be computed in _sample_forward
+            self.baseline_obj, obj = obj_full[..., :-q], obj_full[..., -q:]
+            self.baseline_samples = samples_full[..., :-q, :]
+        else:
+            # handle one-to-many input transforms
+            n_plus_q = X_full.shape[-2]
+            n_w = posterior._extended_shape()[-2] // n_plus_q
+            q_in = q * n_w
+            self._set_sampler(q_in=q_in, posterior=posterior)
+            samples = self._get_f_X_samples(posterior=posterior, q_in=q_in)
+            obj = self.objective(samples, X=X_full[..., -q:, :])
+
+        return samples, obj
+
+    def _compute_best_feasible_objective(self, samples: Tensor, obj: Tensor) -> Tensor:
+        r"""Computes best feasible objective value from samples.
+
+        Args:
+            samples: `sample_shape x batch_shape x q x m`-dim posterior samples.
+            obj: A `sample_shape x batch_shape x q`-dim Tensor of MC objective values.
+
+        Returns:
+            A `sample_shape x batch_shape`-dim Tensor of best feasible objectives.
+        """
+        return compute_best_feasible_objective(
+            samples=samples,
+            obj=obj,
+            constraints=self._constraints,
+            model=self.model,
+            objective=self.objective,
+            posterior_transform=self.posterior_transform,
+            X_baseline=self.X_baseline,
+        )
+
+
+
+[docs] +class qProbabilityOfImprovement(SampleReducingMCAcquisitionFunction): + r"""MC-based batch Probability of Improvement. + + Estimates the probability of improvement over the current best observed + value by sampling from the joint posterior distribution of the q-batch. + MC-based estimates of a probability involves taking expectation of an + indicator function; to support auto-differentiation, the indicator is + replaced with a sigmoid function with temperature parameter `tau`. + + `qPI(X) = P(max Y >= best_f), Y ~ f(X), X = (x_1,...,x_q)` + + Example: + >>> model = SingleTaskGP(train_X, train_Y) + >>> best_f = train_Y.max()[0] + >>> sampler = SobolQMCNormalSampler(1024) + >>> qPI = qProbabilityOfImprovement(model, best_f, sampler) + >>> qpi = qPI(test_X) + """ + + def __init__( + self, + model: Model, + best_f: Union[float, Tensor], + sampler: Optional[MCSampler] = None, + objective: Optional[MCAcquisitionObjective] = None, + posterior_transform: Optional[PosteriorTransform] = None, + X_pending: Optional[Tensor] = None, + tau: float = 1e-3, + constraints: Optional[List[Callable[[Tensor], Tensor]]] = None, + eta: Union[Tensor, float] = 1e-3, + ) -> None: + r"""q-Probability of Improvement. + + Args: + model: A fitted model. + best_f: The best objective value observed so far (assumed noiseless). Can + be a `batch_shape`-shaped tensor, which in case of a batched model + specifies potentially different values for each element of the batch. + sampler: The sampler used to draw base samples. See `MCAcquisitionFunction` + more details. + objective: The MCAcquisitionObjective under which the samples are + evaluated. Defaults to `IdentityMCObjective()`. + NOTE: `ConstrainedMCObjective` for outcome constraints is deprecated in + favor of passing the `constraints` directly to this constructor. + posterior_transform: A PosteriorTransform (optional). + X_pending: A `m x d`-dim Tensor of `m` design points that have + points that have been submitted for function evaluation + but have not yet been evaluated. Concatenated into X upon + forward call. Copied and set to have no gradient. + tau: The temperature parameter used in the sigmoid approximation + of the step function. Smaller values yield more accurate + approximations of the function, but result in gradients + estimates with higher variance. + constraints: A list of constraint callables which map posterior samples to + a scalar. The associated constraint is considered satisfied if this + scalar is less than zero. + eta: Temperature parameter(s) governing the smoothness of the sigmoid + approximation to the constraint indicators. For more details, on this + parameter, see the docs of `compute_smoothed_feasibility_indicator`. + """ + super().__init__( + model=model, + sampler=sampler, + objective=objective, + posterior_transform=posterior_transform, + X_pending=X_pending, + constraints=constraints, + eta=eta, + ) + best_f = torch.as_tensor(best_f, dtype=float).unsqueeze(-1) # adding batch dim + self.register_buffer("best_f", best_f) + self.register_buffer("tau", torch.as_tensor(tau, dtype=float)) + + def _sample_forward(self, obj: Tensor) -> Tensor: + r"""Evaluate qProbabilityOfImprovement per sample on the candidate set `X`. + + Args: + obj: A `sample_shape x batch_shape x q`-dim Tensor of MC objective values. + + Returns: + A `sample_shape x batch_shape x q`-dim Tensor of improvement indicators. + """ + improvement = obj - self.best_f.to(obj) + return torch.sigmoid(improvement / self.tau)
+ + + +
+[docs] +class qSimpleRegret(SampleReducingMCAcquisitionFunction): + r"""MC-based batch Simple Regret. + + Samples from the joint posterior over the q-batch and computes the simple regret. + + `qSR(X) = E(max Y), Y ~ f(X), X = (x_1,...,x_q)` + + Constraints should be provided as a `ConstrainedMCObjective`. + Passing `constraints` as an argument is not supported. This is because + `SampleReducingMCAcquisitionFunction` computes the acquisition values on the sample + level and then weights the sample-level acquisition values by a soft feasibility + indicator. Hence, it expects non-log acquisition function values to be + non-negative. `qSimpleRegret` acquisition values can be negative, so we instead use + a `ConstrainedMCObjective` which applies constraints to the objectives (e.g. before + computing the acquisition function) and shifts negative objective values using + by an infeasible cost to ensure non-negativity (before applying constraints and + shifting them back). + + Example: + >>> model = SingleTaskGP(train_X, train_Y) + >>> sampler = SobolQMCNormalSampler(1024) + >>> qSR = qSimpleRegret(model, sampler) + >>> qsr = qSR(test_X) + """ + + def __init__( + self, + model: Model, + sampler: Optional[MCSampler] = None, + objective: Optional[MCAcquisitionObjective] = None, + posterior_transform: Optional[PosteriorTransform] = None, + X_pending: Optional[Tensor] = None, + ) -> None: + r"""q-Simple Regret. + + Args: + model: A fitted model. + sampler: The sampler used to draw base samples. See `MCAcquisitionFunction` + more details. + objective: The MCAcquisitionObjective under which the samples are + evaluated. Defaults to `IdentityMCObjective()`. + posterior_transform: A PosteriorTransform (optional). + X_pending: A `m x d`-dim Tensor of `m` design points that have + points that have been submitted for function evaluation + but have not yet been evaluated. Concatenated into X upon + forward call. Copied and set to have no gradient. + """ + super().__init__( + model=model, + sampler=sampler, + objective=objective, + posterior_transform=posterior_transform, + X_pending=X_pending, + ) + + def _sample_forward(self, obj: Tensor) -> Tensor: + r"""Evaluate qSimpleRegret per sample on the candidate set `X`. + + Args: + obj: A `sample_shape x batch_shape x q`-dim Tensor of MC objective values. + + Returns: + A `sample_shape x batch_shape x q`-dim Tensor of simple regret values. + """ + return obj
+ + + +
+[docs] +class qUpperConfidenceBound(SampleReducingMCAcquisitionFunction): + r"""MC-based batch Upper Confidence Bound. + + Uses a reparameterization to extend UCB to qUCB for q > 1 (See Appendix A + of [Wilson2017reparam].) + + `qUCB = E(max(mu + |Y_tilde - mu|))`, where `Y_tilde ~ N(mu, beta pi/2 Sigma)` + and `f(X)` has distribution `N(mu, Sigma)`. + + Constraints should be provided as a `ConstrainedMCObjective`. + Passing `constraints` as an argument is not supported. This is because + `SampleReducingMCAcquisitionFunction` computes the acquisition values on the sample + level and then weights the sample-level acquisition values by a soft feasibility + indicator. Hence, it expects non-log acquisition function values to be + non-negative. `qSimpleRegret` acquisition values can be negative, so we instead use + a `ConstrainedMCObjective` which applies constraints to the objectives (e.g. before + computing the acquisition function) and shifts negative objective values using + by an infeasible cost to ensure non-negativity (before applying constraints and + shifting them back). + + Example: + >>> model = SingleTaskGP(train_X, train_Y) + >>> sampler = SobolQMCNormalSampler(1024) + >>> qUCB = qUpperConfidenceBound(model, 0.1, sampler) + >>> qucb = qUCB(test_X) + """ + + def __init__( + self, + model: Model, + beta: float, + sampler: Optional[MCSampler] = None, + objective: Optional[MCAcquisitionObjective] = None, + posterior_transform: Optional[PosteriorTransform] = None, + X_pending: Optional[Tensor] = None, + ) -> None: + r"""q-Upper Confidence Bound. + + Args: + model: A fitted model. + beta: Controls tradeoff between mean and standard deviation in UCB. + sampler: The sampler used to draw base samples. See `MCAcquisitionFunction` + more details. + objective: The MCAcquisitionObjective under which the samples are + evaluated. Defaults to `IdentityMCObjective()`. + posterior_transform: A PosteriorTransform (optional). + X_pending: A `batch_shape x m x d`-dim Tensor of `m` design points that have + points that have been submitted for function evaluation but have not yet + been evaluated. Concatenated into X upon forward call. Copied and set to + have no gradient. + """ + super().__init__( + model=model, + sampler=sampler, + objective=objective, + posterior_transform=posterior_transform, + X_pending=X_pending, + ) + self.beta_prime = math.sqrt(beta * math.pi / 2) + + def _sample_forward(self, obj: Tensor) -> Tensor: + r"""Evaluate qUpperConfidenceBound per sample on the candidate set `X`. + + Args: + obj: A `sample_shape x batch_shape x q`-dim Tensor of MC objective values. + + Returns: + A `sample_shape x batch_shape x q`-dim Tensor of acquisition values. + """ + mean = obj.mean(dim=0) + return mean + self.beta_prime * (obj - mean).abs()
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/botorch/acquisition/multi_objective/logei.html b/_modules/botorch/acquisition/multi_objective/logei.html new file mode 100644 index 0000000..fc2d553 --- /dev/null +++ b/_modules/botorch/acquisition/multi_objective/logei.html @@ -0,0 +1,974 @@ + + + + + + + + + + botorch.acquisition.multi_objective.logei — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for botorch.acquisition.multi_objective.logei

+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# This source code is licensed under the MIT license found in the
+# LICENSE file in the root directory of this source tree.
+
+r"""
+Multi-objective variants of the LogEI family of acquisition functions, see
+[Ament2023logei]_ for details.
+"""
+
+from __future__ import annotations
+
+from typing import Callable, List, Optional, Tuple, Union
+
+import torch
+from botorch.acquisition.logei import TAU_MAX, TAU_RELU
+from botorch.acquisition.multi_objective import MultiObjectiveMCAcquisitionFunction
+from botorch.acquisition.multi_objective.objective import MCMultiOutputObjective
+from botorch.models.model import Model
+from botorch.sampling.base import MCSampler
+from botorch.utils.multi_objective.box_decompositions.non_dominated import (
+    NondominatedPartitioning,
+)
+from botorch.utils.multi_objective.hypervolume import (
+    NoisyExpectedHypervolumeMixin,
+    SubsetIndexCachingMixin,
+)
+from botorch.utils.objective import compute_smoothed_feasibility_indicator
+from botorch.utils.safe_math import (
+    fatmin,
+    log_fatplus,
+    log_softplus,
+    logdiffexp,
+    logmeanexp,
+    logplusexp,
+    logsumexp,
+    smooth_amin,
+)
+from botorch.utils.transforms import (
+    concatenate_pending_points,
+    is_ensemble,
+    match_batch_shape,
+    t_batch_mode_transform,
+)
+from torch import Tensor
+
+
+
+[docs] +class qLogExpectedHypervolumeImprovement( + MultiObjectiveMCAcquisitionFunction, SubsetIndexCachingMixin +): + + _log: bool = True + + def __init__( + self, + model: Model, + ref_point: Union[List[float], Tensor], + partitioning: NondominatedPartitioning, + sampler: Optional[MCSampler] = None, + objective: Optional[MCMultiOutputObjective] = None, + constraints: Optional[List[Callable[[Tensor], Tensor]]] = None, + X_pending: Optional[Tensor] = None, + eta: Optional[Union[Tensor, float]] = 1e-2, + fat: bool = True, + tau_relu: float = TAU_RELU, + tau_max: float = TAU_MAX, + ) -> None: + r"""Parallel Log Expected Hypervolume Improvement supporting m>=2 outcomes. + + See [Ament2023logei]_ for details and the methodology behind the LogEI family of + acquisition function. Line-by-line differences to the original differentiable + expected hypervolume formulation of [Daulton2020qehvi]_ are described via inline + comments in `forward`. + + Example: + >>> model = SingleTaskGP(train_X, train_Y) + >>> ref_point = [0.0, 0.0] + >>> acq = qLogExpectedHypervolumeImprovement(model, ref_point, partitioning) + >>> value = acq(test_X) + + Args: + model: A fitted model. + ref_point: A list or tensor with `m` elements representing the reference + point (in the outcome space) w.r.t. to which compute the hypervolume. + This is a reference point for the objective values (i.e. after + applying`objective` to the samples). + partitioning: A `NondominatedPartitioning` module that provides the non- + dominated front and a partitioning of the non-dominated space in hyper- + rectangles. If constraints are present, this partitioning must only + include feasible points. + sampler: The sampler used to draw base samples. If not given, + a sampler is generated using `get_sampler`. + objective: The MCMultiOutputObjective under which the samples are evaluated. + Defaults to `IdentityMultiOutputObjective()`. + constraints: A list of callables, each mapping a Tensor of dimension + `sample_shape x batch-shape x q x m` to a Tensor of dimension + `sample_shape x batch-shape x q`, where negative values imply + feasibility. The acquisition function will compute expected feasible + hypervolume. + X_pending: A `batch_shape x m x d`-dim Tensor of `m` design points that have + points that have been submitted for function evaluation but have not yet + been evaluated. Concatenated into `X` upon forward call. Copied and set + to have no gradient. + eta: The temperature parameter for the sigmoid function used for the + differentiable approximation of the constraints. In case of a float the + same eta is used for every constraint in constraints. In case of a + tensor the length of the tensor must match the number of provided + constraints. The i-th constraint is then estimated with the i-th + eta value. + fat: Toggles the logarithmic / linear asymptotic behavior of the smooth + approximation to the ReLU and the maximum. + tau_relu: Temperature parameter controlling the sharpness of the + approximation to the ReLU over the `q` candidate points. For further + details, see the comments above the definition of `TAU_RELU`. + tau_max: Temperature parameter controlling the sharpness of the + approximation to the `max` operator over the `q` candidate points. + For further details, see the comments above the definition of `TAU_MAX`. + """ + if len(ref_point) != partitioning.num_outcomes: + raise ValueError( + "The dimensionality of the reference point must match the number of " + f"outcomes. Got ref_point with {len(ref_point)} elements, but expected " + f"{partitioning.num_outcomes}." + ) + ref_point = torch.as_tensor( + ref_point, + dtype=partitioning.pareto_Y.dtype, + device=partitioning.pareto_Y.device, + ) + super().__init__( + model=model, + sampler=sampler, + objective=objective, + constraints=constraints, + eta=eta, + X_pending=X_pending, + ) + self.register_buffer("ref_point", ref_point) + cell_bounds = partitioning.get_hypercell_bounds() + self.register_buffer("cell_lower_bounds", cell_bounds[0]) + self.register_buffer("cell_upper_bounds", cell_bounds[1]) + SubsetIndexCachingMixin.__init__(self) + self.tau_relu = tau_relu + self.tau_max = tau_max + self.fat = fat + + def _compute_log_qehvi(self, samples: Tensor, X: Optional[Tensor] = None) -> Tensor: + r"""Compute the expected (feasible) hypervolume improvement given MC samples. + + Args: + samples: A `sample_shape x batch_shape x q' x m`-dim tensor of samples. + X: A `batch_shape x q x d`-dim tensor of inputs. + + Returns: + A `batch_shape x (model_batch_shape)`-dim tensor of expected hypervolume + improvement for each batch. + """ + # Note that the objective may subset the outcomes (e.g. this will usually happen + # if there are constraints present). + obj = self.objective(samples, X=X) # mc_samples x batch_shape x q x m + q = obj.shape[-2] + if self.constraints is not None: + log_feas_weights = compute_smoothed_feasibility_indicator( + constraints=self.constraints, + samples=samples, + eta=self.eta, + log=True, + fat=self.fat, + ) + device = self.ref_point.device + q_subset_indices = self.compute_q_subset_indices(q_out=q, device=device) + batch_shape = obj.shape[:-2] # mc_samples x batch_shape + # areas tensor is `mc_samples x batch_shape x num_cells x 2`-dim + log_areas_per_segment = torch.full( + size=( + *batch_shape, + self.cell_lower_bounds.shape[-2], # num_cells + 2, # for even and odd terms + ), + fill_value=-torch.inf, + dtype=obj.dtype, + device=device, + ) + + cell_batch_ndim = self.cell_lower_bounds.ndim - 2 + # conditionally adding mc_samples dim if cell_batch_ndim > 0 + # adding ones to shape equal in number to to batch_shape_ndim - cell_batch_ndim + # adding cell_bounds batch shape w/o 1st dimension + sample_batch_view_shape = torch.Size( + [ + batch_shape[0] if cell_batch_ndim > 0 else 1, + *[1 for _ in range(len(batch_shape) - max(cell_batch_ndim, 1))], + *self.cell_lower_bounds.shape[1:-2], + ] + ) + view_shape = ( + *sample_batch_view_shape, + self.cell_upper_bounds.shape[-2], # num_cells + 1, # adding for q_choose_i dimension + self.cell_upper_bounds.shape[-1], # num_objectives + ) + + for i in range(1, self.q_out + 1): + # TODO: we could use batches to compute (q choose i) and (q choose q-i) + # simultaneously since subsets of size i and q-i have the same number of + # elements. This would decrease the number of iterations, but increase + # memory usage. + q_choose_i = q_subset_indices[f"q_choose_{i}"] # q_choose_i x i + # this tensor is mc_samples x batch_shape x i x q_choose_i x m + obj_subsets = obj.index_select(dim=-2, index=q_choose_i.view(-1)) + obj_subsets = obj_subsets.view( + obj.shape[:-2] + q_choose_i.shape + obj.shape[-1:] + ) # mc_samples x batch_shape x q_choose_i x i x m + + # NOTE: the order of operations in non-log _compute_qehvi is 3), 1), 2). + # since 3) moved above 1), _log_improvement adds another Tensor dimension + # that keeps track of num_cells. + + # 1) computes log smoothed improvement over the cell lower bounds. + # mc_samples x batch_shape x num_cells x q_choose_i x i x m + log_improvement_i = self._log_improvement(obj_subsets, view_shape) + + # 2) take the minimum log improvement over all i subsets. + # since all hyperrectangles share one vertex, the opposite vertex of the + # overlap is given by the component-wise minimum. + # negative of maximum of negative log_improvement is approximation to min. + log_improvement_i = self._smooth_min( + log_improvement_i, + dim=-2, + ) # mc_samples x batch_shape x num_cells x q_choose_i x m + + # 3) compute the log lengths of the cells' sides. + # mc_samples x batch_shape x num_cells x q_choose_i x m + log_lengths_i = self._log_cell_lengths(log_improvement_i, view_shape) + + # 4) take product over hyperrectangle side lengths to compute area (m-dim). + # after, log_areas_i is mc_samples x batch_shape x num_cells x q_choose_i + log_areas_i = log_lengths_i.sum(dim=-1) # areas_i = lengths_i.prod(dim=-1) + + # 5) if constraints are present, apply a differentiable approximation of + # the indicator function. + if self.constraints is not None: + log_feas_subsets = log_feas_weights.index_select( + dim=-1, index=q_choose_i.view(-1) + ).view(log_feas_weights.shape[:-1] + q_choose_i.shape) + log_areas_i = log_areas_i + log_feas_subsets.unsqueeze(-3).sum(dim=-1) + + # 6) sum over all subsets of size i, i.e. reduce over q_choose_i-dim + # after, log_areas_i is mc_samples x batch_shape x num_cells + log_areas_i = logsumexp(log_areas_i, dim=-1) # areas_i.sum(dim=-1) + + # 7) Using the inclusion-exclusion principle, set the sign to be positive + # for subsets of odd sizes and negative for subsets of even size + # in non-log space: areas_per_segment += (-1) ** (i + 1) * areas_i, + # but here in log space, we need to keep track of sign: + log_areas_per_segment[..., i % 2] = logplusexp( + log_areas_per_segment[..., i % 2], + log_areas_i, + ) + + # 8) subtract even from odd log area terms + log_areas_per_segment = logdiffexp( + log_a=log_areas_per_segment[..., 0], log_b=log_areas_per_segment[..., 1] + ) + + # 9) sum over segments (n_cells-dim) and average over MC samples + return logmeanexp(logsumexp(log_areas_per_segment, dim=-1), dim=0) + + def _log_improvement( + self, obj_subsets: Tensor, view_shape: Union[Tuple, torch.Size] + ) -> Tensor: + # smooth out the clamp and take the log (previous step 3) + # subtract cell lower bounds, clamp min at zero, but first + # make obj_subsets broadcastable with cell bounds: + # mc_samples x batch_shape x (num_cells = 1) x q_choose_i x i x m + obj_subsets = obj_subsets.unsqueeze(-4) + # making cell bounds broadcastable with obj_subsets: + # (mc_samples = 1) x (batch_shape = 1) x num_cells x 1 x (i = 1) x m + cell_lower_bounds = self.cell_lower_bounds.view(view_shape).unsqueeze(-3) + Z = obj_subsets - cell_lower_bounds + log_Zi = self._log_smooth_relu(Z) + return log_Zi # mc_samples x batch_shape x num_cells x q_choose_i x i x m + + def _log_cell_lengths( + self, log_improvement_i: Tensor, view_shape: Union[Tuple, torch.Size] + ) -> Tensor: + cell_upper_bounds = self.cell_upper_bounds.clamp_max( + 1e10 if log_improvement_i.dtype == torch.double else 1e8 + ) # num_cells x num_objectives + # add batch-dim to compute area for each segment (pseudo-pareto-vertex) + log_cell_lengths = ( + (cell_upper_bounds - self.cell_lower_bounds).log().view(view_shape) + ) # (mc_samples = 1) x (batch_shape = 1) x n_cells x (q_choose_i = 1) x m + # mc_samples x batch_shape x num_cells x q_choose_i x m + return self._smooth_minimum( + log_improvement_i, + log_cell_lengths, + ) + + def _log_smooth_relu(self, X: Tensor) -> Tensor: + f = log_fatplus if self.fat else log_softplus + return f(X, tau=self.tau_relu) + + def _smooth_min(self, X: Tensor, dim: int, keepdim: bool = False) -> Tensor: + f = fatmin if self.fat else smooth_amin + return f(X, tau=self.tau_max, dim=dim) + + def _smooth_minimum(self, X: Tensor, Y: Tensor) -> Tensor: + XY = torch.stack(torch.broadcast_tensors(X, Y), dim=-1) + return self._smooth_min(XY, dim=-1, keepdim=False) + +
+[docs] + @concatenate_pending_points + @t_batch_mode_transform() + def forward(self, X: Tensor) -> Tensor: + posterior = self.model.posterior(X) + samples = self.get_posterior_samples(posterior) + return self._compute_log_qehvi(samples=samples, X=X)
+
+ + + +
+[docs] +class qLogNoisyExpectedHypervolumeImprovement( + NoisyExpectedHypervolumeMixin, + qLogExpectedHypervolumeImprovement, +): + + _log: bool = True + + def __init__( + self, + model: Model, + ref_point: Union[List[float], Tensor], + X_baseline: Tensor, + sampler: Optional[MCSampler] = None, + objective: Optional[MCMultiOutputObjective] = None, + constraints: Optional[List[Callable[[Tensor], Tensor]]] = None, + X_pending: Optional[Tensor] = None, + eta: Optional[Union[Tensor, float]] = 1e-3, + prune_baseline: bool = False, + alpha: float = 0.0, + cache_pending: bool = True, + max_iep: int = 0, + incremental_nehvi: bool = True, + cache_root: bool = True, + tau_relu: float = TAU_RELU, + tau_max: float = 1e-3, # TAU_MAX, + fat: bool = True, + marginalize_dim: Optional[int] = None, + ) -> None: + r""" + q-Log Noisy Expected Hypervolume Improvement supporting m>=2 outcomes. + + Based on the differentiable hypervolume formulation of [Daulton2021nehvi]_. + + Example: + >>> model = SingleTaskGP(train_X, train_Y) + >>> ref_point = [0.0, 0.0] + >>> qNEHVI = qNoisyExpectedHypervolumeImprovement(model, ref_point, train_X) + >>> qnehvi = qNEHVI(test_X) + + Args: + model: A fitted model. + ref_point: A list or tensor with `m` elements representing the reference + point (in the outcome space) w.r.t. to which compute the hypervolume. + This is a reference point for the objective values (i.e. after + applying `objective` to the samples). + X_baseline: A `r x d`-dim Tensor of `r` design points that have already + been observed. These points are considered as potential approximate + pareto-optimal design points. + sampler: The sampler used to draw base samples. If not given, + a sampler is generated using `get_sampler`. + Note: a pareto front is created for each mc sample, which can be + computationally intensive for `m` > 2. + objective: The MCMultiOutputObjective under which the samples are + evaluated. Defaults to `IdentityMultiOutputObjective()`. + constraints: A list of callables, each mapping a Tensor of dimension + `sample_shape x batch-shape x q x m` to a Tensor of dimension + `sample_shape x batch-shape x q`, where negative values imply + feasibility. The acquisition function will compute expected feasible + hypervolume. + X_pending: A `batch_shape x m x d`-dim Tensor of `m` design points that + have points that have been submitted for function evaluation, but + have not yet been evaluated. + eta: The temperature parameter for the sigmoid function used for the + differentiable approximation of the constraints. In case of a float the + same `eta` is used for every constraint in constraints. In case of a + tensor the length of the tensor must match the number of provided + constraints. The i-th constraint is then estimated with the i-th + `eta` value. + prune_baseline: If True, remove points in `X_baseline` that are + highly unlikely to be the pareto optimal and better than the + reference point. This can significantly improve computation time and + is generally recommended. In order to customize pruning parameters, + instead manually call `prune_inferior_points_multi_objective` on + `X_baseline` before instantiating the acquisition function. + alpha: The hyperparameter controlling the approximate non-dominated + partitioning. The default value of 0.0 means an exact partitioning + is used. As the number of objectives `m` increases, consider increasing + this parameter in order to limit computational complexity. + cache_pending: A boolean indicating whether to use cached box + decompositions (CBD) for handling pending points. This is + generally recommended. + max_iep: The maximum number of pending points before the box + decompositions will be recomputed. + incremental_nehvi: A boolean indicating whether to compute the + incremental NEHVI from the `i`th point where `i=1, ..., q` + under sequential greedy optimization, or the full qNEHVI over + `q` points. + cache_root: A boolean indicating whether to cache the root + decomposition over `X_baseline` and use low-rank updates. + marginalize_dim: A batch dimension that should be marginalized. + """ + MultiObjectiveMCAcquisitionFunction.__init__( + self, + model=model, + sampler=sampler, + objective=objective, + constraints=constraints, + eta=eta, + ) + SubsetIndexCachingMixin.__init__(self) + NoisyExpectedHypervolumeMixin.__init__( + self, + model=model, + ref_point=ref_point, + X_baseline=X_baseline, + sampler=sampler, + objective=objective, + constraints=constraints, + X_pending=X_pending, + prune_baseline=prune_baseline, + alpha=alpha, + cache_pending=cache_pending, + max_iep=max_iep, + incremental_nehvi=incremental_nehvi, + cache_root=cache_root, + marginalize_dim=marginalize_dim, + ) + # parameters that are used by qLogEHVI + self.tau_relu = tau_relu + self.tau_max = tau_max + self.fat = fat + +
+[docs] + @concatenate_pending_points + @t_batch_mode_transform() + def forward(self, X: Tensor) -> Tensor: + X_full = torch.cat([match_batch_shape(self.X_baseline, X), X], dim=-2) + # NOTE: To ensure that we correctly sample `f(X)` from the joint distribution + # `f((X_baseline, X)) ~ P(f | D)`, it is critical to compute the joint posterior + # over X *and* X_baseline -- which also contains pending points whenever there + # are any -- since the baseline and pending values `f(X_baseline)` are + # generally pre-computed and cached before the `forward` call, see the docs of + # `cache_pending` for details. + # TODO: Improve the efficiency by not re-computing the X_baseline-X_baseline + # covariance matrix, but only the covariance of + # 1) X and X, and + # 2) X and X_baseline. + posterior = self.model.posterior(X_full) + # Account for possible one-to-many transform and the model batch dimensions in + # ensemble models. + event_shape_lag = 1 if is_ensemble(self.model) else 2 + n_w = ( + posterior._extended_shape()[X_full.dim() - event_shape_lag] + // X_full.shape[-2] + ) + q_in = X.shape[-2] * n_w + self._set_sampler(q_in=q_in, posterior=posterior) + samples = self._get_f_X_samples(posterior=posterior, q_in=q_in) + # Add previous nehvi from pending points. + nehvi = self._compute_log_qehvi(samples=samples, X=X) + if self.incremental_nehvi: + return nehvi + return logplusexp(nehvi, self._prev_nehvi.log())
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/botorch/acquisition/multi_objective/parego.html b/_modules/botorch/acquisition/multi_objective/parego.html new file mode 100644 index 0000000..d8cd067 --- /dev/null +++ b/_modules/botorch/acquisition/multi_objective/parego.html @@ -0,0 +1,641 @@ + + + + + + + + + + botorch.acquisition.multi_objective.parego — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for botorch.acquisition.multi_objective.parego

+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# This source code is licensed under the MIT license found in the
+# LICENSE file in the root directory of this source tree.
+
+from typing import Callable, List, Optional, Union
+
+import torch
+from botorch.acquisition.logei import qLogNoisyExpectedImprovement, TAU_MAX, TAU_RELU
+from botorch.acquisition.multi_objective.monte_carlo import (
+    MultiObjectiveMCAcquisitionFunction,
+)
+from botorch.acquisition.multi_objective.objective import MCMultiOutputObjective
+from botorch.acquisition.objective import GenericMCObjective
+from botorch.models.model import Model
+from botorch.posteriors.fully_bayesian import MCMC_DIM
+from botorch.sampling.base import MCSampler
+from botorch.utils.multi_objective.scalarization import get_chebyshev_scalarization
+from botorch.utils.sampling import sample_simplex
+from botorch.utils.transforms import is_ensemble
+from torch import Tensor
+
+
+
+[docs] +class qLogNParEGO(qLogNoisyExpectedImprovement, MultiObjectiveMCAcquisitionFunction): + def __init__( + self, + model: Model, + X_baseline: Tensor, + scalarization_weights: Optional[Tensor] = None, + sampler: Optional[MCSampler] = None, + objective: Optional[MCMultiOutputObjective] = None, + constraints: Optional[List[Callable[[Tensor], Tensor]]] = None, + X_pending: Optional[Tensor] = None, + eta: Union[Tensor, float] = 1e-3, + fat: bool = True, + prune_baseline: bool = False, + cache_root: bool = True, + tau_relu: float = TAU_RELU, + tau_max: float = TAU_MAX, + ) -> None: + r"""q-LogNParEGO supporting m >= 2 outcomes. This acquisition function + utilizes qLogNEI to compute the expected improvement over Chebyshev + scalarization of the objectives. + + This is adapted from qNParEGO proposed in [Daulton2020qehvi]_ to utilize + log-improvement acquisition functions of [Ament2023logei]_. See [Knowles2005]_ + for the original ParEGO algorithm. + + This implementation assumes maximization of all objectives. If any of the model + outputs are to be minimized, either an `objective` should be used to negate the + model outputs or the `scalarization_weights` should be provided with negative + weights for the outputs to be minimized. + + Args: + model: A fitted multi-output model, producing outputs for `m` objectives + and any number of outcome constraints. + NOTE: The model posterior must have a `mean` attribute. + X_baseline: A `batch_shape x r x d`-dim Tensor of `r` design points + that have already been observed. These points are considered as + the potential best design point. + scalarization_weights: A `m`-dim Tensor of weights to be used in the + Chebyshev scalarization. If omitted, samples from the unit simplex. + sampler: The sampler used to draw base samples. See `MCAcquisitionFunction` + more details. + objective: The MultiOutputMCAcquisitionObjective under which the samples are + evaluated before applying Chebyshev scalarization. + Defaults to `IdentityMultiOutputObjective()`. + constraints: A list of constraint callables which map a Tensor of posterior + samples of dimension `sample_shape x batch-shape x q x m'`-dim to a + `sample_shape x batch-shape x q`-dim Tensor. The associated constraints + are satisfied if `constraint(samples) < 0`. + X_pending: A `batch_shape x q' x d`-dim Tensor of `q'` design points + that have points that have been submitted for function evaluation + but have not yet been evaluated. Concatenated into `X` upon + forward call. Copied and set to have no gradient. + eta: Temperature parameter(s) governing the smoothness of the sigmoid + approximation to the constraint indicators. See the docs of + `compute_(log_)smoothed_constraint_indicator` for details. + fat: Toggles the logarithmic / linear asymptotic behavior of the smooth + approximation to the ReLU. + prune_baseline: If True, remove points in `X_baseline` that are + highly unlikely to be the best point. This can significantly + improve performance and is generally recommended. In order to + customize pruning parameters, instead manually call + `botorch.acquisition.utils.prune_inferior_points` on `X_baseline` + before instantiating the acquisition function. + cache_root: A boolean indicating whether to cache the root + decomposition over `X_baseline` and use low-rank updates. + tau_max: Temperature parameter controlling the sharpness of the smooth + approximations to max. + tau_relu: Temperature parameter controlling the sharpness of the smooth + approximations to ReLU. + """ + MultiObjectiveMCAcquisitionFunction.__init__( + self, + model=model, + sampler=sampler, + objective=objective, + constraints=constraints, + eta=eta, + ) + org_objective = self.objective + # Create the composite objective. + with torch.no_grad(): + Y_baseline = org_objective(model.posterior(X_baseline).mean) + if is_ensemble(model): + Y_baseline = torch.mean(Y_baseline, dim=MCMC_DIM) + scalarization_weights = ( + scalarization_weights + if scalarization_weights is not None + else sample_simplex( + d=Y_baseline.shape[-1], device=X_baseline.device, dtype=X_baseline.dtype + ).view(-1) + ) + chebyshev_scalarization = get_chebyshev_scalarization( + weights=scalarization_weights, + Y=Y_baseline, + ) + composite_objective = GenericMCObjective( + objective=lambda samples, X=None: chebyshev_scalarization( + org_objective(samples=samples, X=X), X=X + ), + ) + qLogNoisyExpectedImprovement.__init__( + self, + model=model, + X_baseline=X_baseline, + sampler=sampler, + # This overwrites self.objective with the composite objective. + objective=composite_objective, + X_pending=X_pending, + constraints=constraints, + eta=eta, + fat=fat, + prune_baseline=prune_baseline, + cache_root=cache_root, + tau_max=tau_max, + tau_relu=tau_relu, + ) + # Set these after __init__ calls so that they're not overwritten / deleted. + # These are intended mainly for easier debugging & transparency. + self._org_objective: MCMultiOutputObjective = org_objective + self.chebyshev_scalarization: Callable[[Tensor, Optional[Tensor]], Tensor] = ( + chebyshev_scalarization + ) + self.scalarization_weights: Tensor = scalarization_weights + self.Y_baseline: Tensor = Y_baseline
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/index.html b/_modules/index.html new file mode 100644 index 0000000..41bd82a --- /dev/null +++ b/_modules/index.html @@ -0,0 +1,525 @@ + + + + + + + + + + Overview: module code — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + + + + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/acquisition/custom.html b/_modules/obsidian/acquisition/custom.html new file mode 100644 index 0000000..a4976a9 --- /dev/null +++ b/_modules/obsidian/acquisition/custom.html @@ -0,0 +1,609 @@ + + + + + + + + + + obsidian.acquisition.custom — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.acquisition.custom

+"""Custom implementations of acquisition functions using BoTorch API"""
+
+from botorch.acquisition.monte_carlo import MCAcquisitionFunction
+from botorch.sampling.normal import SobolQMCNormalSampler
+from botorch.utils import t_batch_mode_transform
+from botorch.acquisition.multi_objective.objective import IdentityMCMultiOutputObjective
+from botorch.models.model import Model
+from botorch.sampling.base import MCSampler
+from botorch.acquisition.objective import MCAcquisitionObjective, PosteriorTransform
+
+import torch
+from torch import Tensor
+
+
+
+[docs] +class qMean(MCAcquisitionFunction): + """ + Acquisition function which optimizes for the + maximum value of the posterior mean + """ + def __init__(self, + model: Model, + sampler: MCSampler | None = None, + objective: MCAcquisitionObjective | None = None, + posterior_transform: PosteriorTransform | None = None, + X_pending: Tensor | None = None): + + if sampler is None: + sampler = SobolQMCNormalSampler(sample_shape=torch.Size([512])) + + # An objective is required if this aq is used on a multi-output model + if objective is None: + if model.num_outputs > 1: + objective = IdentityMCMultiOutputObjective() + else: + objective = None # This will be set to identity in super call + + super().__init__(model=model, sampler=sampler, objective=objective, + posterior_transform=posterior_transform, X_pending=X_pending) + +
+[docs] + @t_batch_mode_transform() + def forward(self, + x: Tensor) -> Tensor: + """ + Evaluate the acquisition function on the candidate set x + """ + # x dimensions: b * q * d + posterior = self.model.posterior(x) # dimensions: b * q + samples = self.get_posterior_samples(posterior) # dimensions: n * b * q * o + + o = self.objective(samples, x) + if self.objective._is_mo: + o = o.sum(dim=-1) # Sum over o if objective is MOO + else: + o = samples.sum(dim=-1) # Sum over o if f no objectives + + # Mean over posterior, sum over q in any case + return o.mean(dim=0).sum(dim=-1)
+
+ + + +
+[docs] +class qSpaceFill(MCAcquisitionFunction): + """ + Acquisition function which optimizes for the + maximum value of minimum distance between a point and the training data + """ + def __init__(self, + model: Model, + sampler: MCSampler | None = None, + objective: MCAcquisitionObjective | None = None, + posterior_transform: PosteriorTransform | None = None, + X_pending: Tensor | None = None): + + if sampler is None: + sampler = SobolQMCNormalSampler(sample_shape=torch.Size([512])) + + # An objective is required if this aq is used on a multi-output model + if model.num_outputs > 1: + objective = IdentityMCMultiOutputObjective() + else: + objective = None + + super().__init__(model=model, sampler=sampler, objective=objective, + posterior_transform=posterior_transform, X_pending=X_pending) + +
+[docs] + @t_batch_mode_transform() + def forward(self, + x: Tensor) -> Tensor: + """ + Evaluate the acquisition function on the candidate set x + """ + # x dimensions: b * q * d + x_train = self.model.train_inputs[0][0] # train_inputs is a list of tuples + + # For sequential mode, add pending data points to "train" + if self.X_pending is not None: + x_train = torch.concat((x_train, self.X_pending)) + + x = x.unsqueeze(-2) # Insert expmt dimension before xdim + x_train = x_train.unsqueeze(0).unsqueeze(0) # Add batch and q dimensions + + # Norm distance over xdim + dist = torch.norm(x-x_train, p=2, dim=-1) + + # Min over expmt and q (for joint evaluation) + closest_dist = dist.min(dim=-1)[0].min(dim=-1)[0] + + return closest_dist
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/campaign/analysis.html b/_modules/obsidian/campaign/analysis.html new file mode 100644 index 0000000..947e8d1 --- /dev/null +++ b/_modules/obsidian/campaign/analysis.html @@ -0,0 +1,747 @@ + + + + + + + + + + obsidian.campaign.analysis — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.campaign.analysis

+"Analysis utility functions for examining metrics over the context of an optimization campaign"
+
+from obsidian.parameters import Param_Continuous
+
+import numpy as np
+import pandas as pd
+import matplotlib.pyplot as plt
+
+
+
+[docs] +def plot_ofat_ranges(optimizer, ofat_ranges): + """ + Plots each parameter's 1D OFAT acceptable range + + Args: + optimizer (BayesianOptimizer): The optimizer object which contains a surrogate that has been fit to data + and can be used to make predictions. + ofat_ranges (pd.DataFrame): A DataFrame containing the acceptable range values for each parameter. + + Returns: + fig (matplotlib.figure.Figure): The parameter OFAT acceptable-range plot + """ + + fig = plt.figure(figsize=(8, 4)) + colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] + for i, (index, row) in enumerate(ofat_ranges.iloc[0:10, :].iterrows()): + color = colors[i] + + plt.plot([index, index], [row['Min_LB'], row['Max_LB']], + linewidth=6, linestyle='solid', color=color, label='High Confidence' if i == 0 else None) + if row['Min_LB'] > row['Min_Mu']: + plt.annotate( + f'{(row["Min_LB"]*optimizer.X_space.X_range[index].iloc[0]+optimizer.X_space.X_min[index].iloc[0]):.2f}', + xy=(i, row['Min_LB']), xytext=(i + 0.25, row['Min_LB']), + fontsize=8, ha='left', va='center', rotation=0, arrowprops=dict(arrowstyle='-', color=color, lw=1)) + if row['Max_LB'] < row['Max_Mu']: + plt.annotate( + f'{(row["Max_LB"]*optimizer.X_space.X_range[index].iloc[0]+optimizer.X_space.X_min[index].iloc[0]):.2f}', + xy=(i, row['Max_LB']), xytext=(i + 0.25, row['Max_LB']), + fontsize=8, ha='left', va='center', rotation=0, arrowprops=dict(arrowstyle='-', color=color, lw=1)) + + plt.plot([index, index], [row['Min_Mu'], row['Max_Mu']], linewidth=3, + linestyle='solid', color=color, label='Average' if i == 0 else None) + plt.annotate( + f'{(row["Min_Mu"]*optimizer.X_space.X_range[index].iloc[0]+optimizer.X_space.X_min[index].iloc[0]):.2f}', + xy=(i, row['Min_Mu']), xytext=(i + 0.25, row['Min_Mu']), + fontsize=8, ha='left', va='center', rotation=0, arrowprops=dict(arrowstyle='-', color=color, lw=1)) + plt.annotate( + f'{(row["Max_Mu"]*optimizer.X_space.X_range[index].iloc[0]+optimizer.X_space.X_min[index].iloc[0]):.2f}', + xy=(i, row['Max_Mu']), xytext=(i + 0.25, row['Max_Mu']), + fontsize=8, ha='left', va='center', rotation=0, arrowprops=dict(arrowstyle='-', color=color, lw=1)) + + if row['Min_UB'] < row['Min_Mu']: + plt.plot([index, index], [row['Min_UB'], row['Min_Mu']], linewidth=1, linestyle=':', color=color) + if row['Max_UB'] > row['Max_Mu']: + plt.plot([index, index], [row['Max_UB'], row['Max_Mu']], linewidth=1, linestyle=':', color=color) + plt.plot([0], [0], linewidth=1, linestyle=':', color=color, label='Low Confidence' if i == 0 else None) + + plt.xticks(rotation=90) + plt.ylabel('Parameter Value (Scaled)') + plt.ylim([-0.15, 1.15]) + plt.xlim([-1, i+1]) + plt.title(f'Univeriate Range (OFAT) Estimates from APO Model \n Ranges exceeding {row["Threshold"]} {row["Response"]}', + fontsize=10) + plt.legend(bbox_to_anchor=(1.1, 1.05)) + + return fig
+ + + +
+[docs] +def plot_interactions(optimizer, cor, clamp=False): + """ + Plots the parameter interaction matrix + + Args: + optimizer (BayesianOptimizer): The optimizer object which contains a surrogate that has been fit to data + and can be used to make predictions. + cor (np.ndarray): The correlation matrix representing the parameter interactions. + clamp (bool, optional): Whether to clamp the colorbar range to (0, 1). Defaults to ``False``. + + Returns: + matplotlib.figure.Figure: The parameter interaction plot + """ + fig = plt.figure(figsize=(5, 5)) + ax = fig.gca() + cax = ax.matshow(cor) + if clamp: + cax.set_clim(0, 1) + axis = np.arange(len(optimizer.X_space.X_names)) + names = optimizer.X_space.X_names + ax.set_xticks(axis) + ax.set_xticklabels(names, rotation=90) + ax.set_yticks(axis) + ax.set_yticklabels(names, rotation=0) + cbar = fig.colorbar(cax) + ax.set_title('Parameter Interactions') + cbar.ax.set_ylabel('Range Shrinkage') + for (i, j), z in np.ndenumerate(cor): + if z > 0.05: + ax.text(j, i, '{:0.2f}'.format(z), ha='center', va='center', fontsize=8) + return fig
+ + + +
+[docs] +def calc_ofat_ranges(optimizer, threshold, X_ref, PI_range=0.7, + steps=100, response_id=0, calc_interacts=True): + """ + Calculates an OFAT design space using confidence bounds around the optimizer prediction. Also + includes a matrix of interaction scores. + + Args: + optimizer (BayesianOptimizer): The optimizer object which contains a surrogate that has been fit to data + and can be used to make predictions. + X_ref (pd.DataFrame): The reference data point from which the OFAT variations are calculated. + threshold (float): The response value threshold (minimum value) which would be considered passing for OFAT variations. + PI_range (float, optional): The prediction interval coverage (fraction of density) + steps (int, optional): The number of steps to use in the search for the OFAT boundaries. + The default value is ``100``. + response_id (int, optional): The index of the relevant response within the fitted optimizer object. + The default value is ``0``. + calc_interacts (bool, optional): Whether or not to return the interaction matrix; default is ``True``. + + Returns: + ofat_ranges (pd.DataFrame): A dataframe describing the min/max OFAT values using each LB, UB, and average prediction. + Values are scaled in the (0,1) space based on optimizer.X_space. + cor (np.array): A matrix of interaction values between every combination of two parameters. + Each value is the fractional reduction in size for the acceptable range envelope created by a 2-factor variation, + in comparison to the corresponding two independent 1-factor variations. As such, diagonal elements are 0. + """ + + threshold = 0.4 + ofat_ranges = [] + response_name = optimizer.target[response_id].name + + for p in optimizer.X_space: + if isinstance(p, Param_Continuous): + X_min = p.min + X_max = p.max + X_range = p.range + X_span = np.linspace(X_min, X_max, steps) + + X_sim = pd.DataFrame(np.repeat(X_ref.values, repeats=steps, axis=0), columns=X_ref.columns) + X_sim[p.name] = X_span + df_pred = optimizer.predict(X_sim, PI_range=PI_range) + lb = df_pred[response_name + ' lb'] + ub = df_pred[response_name + ' ub'] + pred_mu = df_pred[response_name + ' (pred)'] + + row = {'Name': p.name, 'PI Range': PI_range, 'Threshold': threshold, 'Response': response_name} + labels = ['Mu', 'LB', 'UB'] + + for label, y in zip(labels, [pred_mu, lb, ub]): + pass_ids = np.where(pred_mu > threshold) + pass_vals = X_sim[p.name].iloc[pass_ids] + + row['Min_'+label] = (pass_vals.min()-X_min)/X_range + row['Max_'+label] = (pass_vals.max()-X_min)/X_range + ofat_ranges.append(row) + + ofat_ranges = pd.DataFrame(ofat_ranges).set_index('Name') + + if calc_interacts: + cor = [] + + for i, pi in enumerate(optimizer.X_space.X_names): + cor_j = [] + + Xi_pass_min = optimizer.X_space.X_min[pi] + optimizer.X_space.X_range[pi]*ofat_ranges['Min_Mu'][pi] + Xi_pass_max = optimizer.X_space.X_min[pi] + optimizer.X_space.X_range[pi]*ofat_ranges['Max_Mu'][pi] + Xi_pass_span = np.linspace(Xi_pass_min, Xi_pass_max, steps) + + for j, pj in enumerate(optimizer.X_space.X_names): + Xj_pass_min = optimizer.X_space.X_min[pj] + optimizer.X_space.X_range[pj]*ofat_ranges['Min_Mu'][pj] + Xj_pass_max = optimizer.X_space.X_min[pj] + optimizer.X_space.X_range[pj]*ofat_ranges['Max_Mu'][pj] + Xj_pass_span = np.linspace(Xj_pass_min, Xj_pass_max, steps) + + X_sim_cor = pd.DataFrame(np.repeat(X_ref.values, repeats=steps, axis=0), columns=X_ref.columns) + + X_sim_cor[pj] = Xj_pass_span + if not pi == pj: + X_sim_cor[pi] = Xi_pass_span + + pred_mu_cor_all, _ = optimizer.predict(X_sim_cor) + pred_mu_cor = pred_mu_cor_all.iloc[:, response_id] + cor_passing = np.where(pred_mu_cor > threshold)[0] + + if len(cor_passing) > 0: + start = cor_passing[0] + stop = cor_passing[-1] + cor_ij = 1-(stop-start)/(steps-1) + cor_j.append(cor_ij) + else: + cor_j.append(0) + + cor.append(cor_j) + cor = np.array(cor) + else: + cor = None + + return ofat_ranges, cor
+ + + +
+[docs] +def sensitivity(optimizer, + dx: float = 1e-6, + X_ref: pd.DataFrame | None = None) -> pd.DataFrame: + """ + Calculates the sensitivity of the surrogate model predictions with respect to each parameter in the X_space. + + Args: + optimizer (BayesianOptimizer): The optimizer object which contains a surrogate that has been fit to data + and can be used to make predictions. + dx (float, optional): The perturbation size for calculating the sensitivity. Defaults to ``1e-6``. + X_ref (pd.DataFrame | None, optional): The reference input values for calculating the sensitivity. + If None, the mean of X_space will be used as the reference. Defaults to ``None``. + + Returns: + pd.DataFrame: A DataFrame containing the sensitivity values for each parameter in X_space. + + Raises: + ValueError: If X_ref does not contain all parameters in optimizer.X_space or if X_ref is not a single row DataFrame. + """ + if X_ref is None: + X_ref = optimizer.X_space.mean() + else: + if not all(x in X_ref.columns for x in optimizer.X_space.X_names): + raise ValueError('X_ref must contain all parameters in X_space') + if X_ref.shape[0] != 1: + raise ValueError('X_ref must be a single row DataFrame') + + y_ref = optimizer.predict(X_ref) + + sens = {} + + # Only do positive perturbation, for simplicity + for param in optimizer.X_space: + base = param.unit_map(X_ref[param.name].values)[0] + # Space already mapped to (0,1), use absolute perturbation + dx_pos = np.array(base+dx).reshape(-1, 1) + X_sim = X_ref.copy() + X_sim[param.name] = param.unit_demap(dx_pos)[0] + y_sim = optimizer.predict(X_sim) + dydx = (y_sim - y_ref)/dx + sens[param.name] = dydx.to_dict('records')[0] + + df_sens = pd.DataFrame(sens).T[[y+' (pred)' for y in optimizer.y_names]] + + return df_sens
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/campaign/campaign.html b/_modules/obsidian/campaign/campaign.html new file mode 100644 index 0000000..1301874 --- /dev/null +++ b/_modules/obsidian/campaign/campaign.html @@ -0,0 +1,926 @@ + + + + + + + + + + obsidian.campaign.campaign — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.campaign.campaign

+"""Campaign class definition"""
+
+from obsidian.parameters import ParamSpace, Target
+from obsidian.optimizer import Optimizer, BayesianOptimizer
+from obsidian.experiment import ExpDesigner
+from obsidian.objectives import Objective, Objective_Sequence, obj_class_dict
+from obsidian.exceptions import IncompatibleObjectiveError
+import obsidian
+
+import pandas as pd
+import torch
+import warnings
+
+
+
+[docs] +class Campaign(): + """ + Base class for tracking optimization progress and other metrics + over multiple iterations. + + Attributes: + X_space (ParamSpace): The parameter space for the campaign. + data (pd.DataFrame): The data collected during the campaign. + optimizer (Optimizer): The optimizer used for optimization. + designer (ExpDesigner): The experimental designer used for experiment design. + iter (int): The current iteration number. + seed (int): The seed for random number generation. + + Properties: + m_exp (int): The number of observations in campaign.data + y (pd.Series): The response data in campaign.data + f (pd.Series): The transformed response data + X (pd.DataFrame): The input features of campaign.data + response_max (float | pd.Series): The maximum for each response + target (Target | list[Target]): The target(s) for optimization. + objective (Objective, optional): The objective of the optimization campaign + + """ + +
+[docs] + def __init__(self, + X_space: ParamSpace, + target: Target | list[Target], + optimizer: Optimizer | None = None, + designer: ExpDesigner | None = None, + objective: Objective | None = None, + seed: int | None = None): + + self.X_space = X_space + self.data = pd.DataFrame() + + optimizer = BayesianOptimizer(X_space, seed=seed) if optimizer is None else optimizer + self.set_optimizer(optimizer) + + designer = ExpDesigner(X_space, seed=seed) if designer is None else designer + self.set_designer(designer) + + self.set_target(target) + self.set_objective(objective) + + # Non-object attributes + self.iter = 0 + self.seed = seed + self.version = obsidian.__version__
+ + +
+[docs] + def add_data(self, df: pd.DataFrame): + """ + Adds data to the campaign. + + Args: + Z_i (pd.DataFrame): The data to be added to the campaign. + + Raises: + KeyError: If all X_names are not in the dataset + KeyError: If all y_names are not in the dataset + """ + + if not all(name in df.columns for name in self.X_space.X_names): + raise KeyError('Input dataset does not contain all of the required parameter names') + if not all(name in df.columns for name in self.y_names): + raise KeyError('Input dataset does not contain all of the required response target names') + + new_data = df + + if 'Iteration' not in new_data.columns: + new_data['Iteration'] = self.iter + else: + self.iter = int(new_data['Iteration'].max()) + + self.iter += 1 + self.data = pd.concat([self.data, new_data], axis=0, ignore_index=True) + self.data.index.name = 'Observation ID' + self.data.index = self.data.index.astype('int') + + if self.optimizer.is_fit: + self._analyze()
+ + +
+[docs] + def clear_data(self): + """Clears campaign data""" + self.data = None
+ + + @property + def optimizer(self) -> Optimizer: + """Campaign Optimizer""" + return self._optimizer + +
+[docs] + def set_optimizer(self, optimizer: Optimizer): + """Sets the campaign optimizer""" + self._optimizer = optimizer
+ + + @property + def designer(self) -> ExpDesigner: + """Campaign Experimental Designer""" + return self._designer + +
+[docs] + def set_designer(self, designer: ExpDesigner): + """Sets the campaign experiment designer""" + self._designer = designer
+ + + @property + def objective(self) -> Objective | None: + """Campaign Objective function""" + return self._objective + + def _eval_objective(self): + """Evaluates objective and appends it to campaign data""" + df_o = self.o + for col in df_o.columns: + self.data[col] = df_o[col].values + +
+[docs] + def set_objective(self, objective: Objective | None): + """(Re)sets the campaign objective function""" + self._objective = objective + if not self.data.empty: + # Remove previous objective evaluations + self.data = self.data.drop( + columns=[col for col in self.data.columns if 'Objective' in col] + ) + if self.optimizer.is_fit: + self._analyze()
+ + + @property + def target(self): + """Campaign experimental target(s)""" + return self._target + +
+[docs] + def set_target(self, + target: Target | list[Target]): + """ + Sets the experimental target context for the campaign. + + Args: + target (Target | list[Target] | None): The target or list of targets to set. + + """ + if isinstance(target, Target): + self._target = [target] + else: + self._target = target + + self.y_names = [t.name for t in self._target] + self.n_response = len(self.y_names)
+ + + @property + def _is_mo(self) -> bool: + """ + Boolean flag for multi-output + """ + if self.objective: + return self.objective._is_mo + else: + return self.n_response > 1 + + @property + def m_exp(self) -> int: + """ + Number of observations in training data + """ + return self.data.shape[0] + + @property + def y(self) -> pd.Series | pd.DataFrame: + """ + Experimental response data + + Raises: + ValueError: If no target(s) are specified. + """ + if not self.target: + raise ValueError('No target(s) specified') + return self.data[self.y_names] + + @property + def response_max(self) -> float | pd.Series: + """ + Maximum response data in training set + """ + return self.y.max() + + @property + def f(self) -> pd.Series | pd.DataFrame: + """ + Experimental response data, in transformed space + """ + f = pd.concat([t.transform_f(self.y[t.name]) for t in self.target], axis=1) + return f + + @property + def o(self) -> pd.Series | pd.DataFrame: + if self.objective: + try: + x = self.X_space.encode(self.data[list(self.X_space.X_names)]).values + o = self.objective(torch.tensor(self.f.values).unsqueeze(0), + X=torch.tensor(x)).squeeze(0) + if o.ndim < 2: + o = o.unsqueeze(1) # Rearrange into m x o + return pd.DataFrame(o.detach().cpu().numpy(), + columns=[f'Objective {o_i+1}' for o_i in range(o.shape[1])]) + except Exception: + raise IncompatibleObjectiveError('Objective(s) did not successfully execute on sample') + else: + return None + + @property + def X(self) -> pd.DataFrame: + """ + Feature columns of the training data + """ + return self.data[self.X_space.X_names] + +
+[docs] + def save_state(self) -> dict: + """ + Saves the state of the Campaign object as a dictionary. + + Returns: + dict: A dictionary containing the saved state of the Campaign object. + """ + obj_dict = {} + obj_dict['X_space'] = self.X_space.save_state() + obj_dict['optimizer'] = self.optimizer.save_state() + obj_dict['data'] = self.data.to_dict() + obj_dict['target'] = [t.save_state() for t in self.target] + if self.objective: + obj_dict['objective'] = self.objective.save_state() + obj_dict['seed'] = self.seed + + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, + obj_dict: dict): + """ + Loads the state of the campaign from a dictionary. + + Args: + cls (Campaign): The class object. + obj_dict (dict): A dictionary containing the campaign state. + + Returns: + Campaign: A new campaign object with the loaded state. + """ + + if 'objective' in obj_dict: + if obj_dict['objective']['name'] == 'Objective_Sequence': + new_objective = Objective_Sequence.load_state(obj_dict['objective']) + else: + obj_class = obj_class_dict[obj_dict['objective']['name']] + new_objective = obj_class.load_state(obj_dict['objective']) + else: + new_objective = None + + new_campaign = cls(X_space=ParamSpace.load_state(obj_dict['X_space']), + target=[Target.load_state(t_dict) for t_dict in obj_dict['target']], + optimizer=BayesianOptimizer.load_state(obj_dict['optimizer']), + objective=new_objective, + seed=obj_dict['seed']) + new_campaign.data = pd.DataFrame(obj_dict['data']) + new_campaign.data.index = new_campaign.data.index.astype('int') + + try: + new_campaign.iter = new_campaign.data['Iteration'].astype('int').max() + except KeyError: + new_campaign.iter = 0 + + return new_campaign
+ + + def __repr__(self): + """String representation of object""" + return f"obsidian Campaign for {getattr(self,'y_names', None)}; {getattr(self,'m_exp', 0)} observations" + +
+[docs] + def initialize(self, + design_kwargs={}): + """ + Maps ExpDesigner.initialize method + """ + return self.designer.initialize(**design_kwargs)
+ + +
+[docs] + def fit(self): + """ + Maps Optimizer.fit method + + Raises: + ValueError: If no data has been registered to the campaign + """ + + if self.m_exp <= 0: + raise ValueError('Must register data before fitting') + + self.optimizer.fit(self.data, target=self.target)
+ + +
+[docs] + def suggest(self, + optim_kwargs={}): + """ + Maps Optimizer.suggest method + """ + if self.optimizer.is_fit: + try: + X, eval = self.optimizer.suggest(objective=self.objective, **optim_kwargs) + return (X, eval) + except Exception: + warnings.warn('Optimization failed') + return None + else: + warnings.warn('Optimizer is not fit to data. Suggesting initial experiments.', UserWarning) + X0 = self.initialize() + return X0
+ + + def _profile_hv(self): + """ + Calculate and assign the hypervolume values to each iteration in the data. + + Returns: + None + """ + iters = self.data['Iteration'].unique() + hv = {} + + if self.objective: + out = self.o + else: + out = self.y + + for i in iters: + iter_index = self.data.query(f'Iteration <= {i}').index + out_iter = out.loc[iter_index, :] + out_iter = torch.tensor(out_iter.values).to(self.optimizer.device) + hv[i] = self.optimizer.hypervolume(out_iter) + + self.data['Hypervolume (iter)'] = self.data.apply(lambda x: hv[x['Iteration']], axis=1) + self.data['Pareto Front'] = self.optimizer.pareto(torch.tensor(out.values).to(self.optimizer.device)) + + return + + def _profile_max(self): + """ + Calculate the maximum values achieved for targets at each iteration + + Returns: + None + """ + + # Remove previous max-profiling + self.data = self.data.drop( + columns=[col for col in self.data.columns if '(max) (iter)' in col] + ) + + if self.objective: + out_names = [col for col in self.data.columns if 'Objective' in col] + else: + out_names = self.y_names + + for out in out_names: + self.data[out+' (max) (iter)'] = self.data.apply( + lambda x: self.data.query(f'Iteration<={x["Iteration"]}')[out].max(), axis=1 + ) + + return + + def _analyze(self): + """ + Analyzes the campaign data for practical optimization performance metrics + + Returns: + None + """ + if self.objective: + self._eval_objective() + self._profile_max() + if self._is_mo: + self._profile_hv() + else: + # Remove previous HV-profiling + self.data = self.data.drop( + columns=[col for col in self.data.columns + if 'Hypervolume' in col or 'Pareto' in col] + ) + + return
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/campaign/explainer.html b/_modules/obsidian/campaign/explainer.html new file mode 100644 index 0000000..f2518cb --- /dev/null +++ b/_modules/obsidian/campaign/explainer.html @@ -0,0 +1,710 @@ + + + + + + + + + + obsidian.campaign.explainer — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.campaign.explainer

+"""Explainer Class: Surrogate Model Interpretation Methods"""
+
+from .analysis import sensitivity
+
+from obsidian.parameters import Param_Continuous, ParamSpace
+from obsidian.optimizer import Optimizer
+
+import shap
+from shap import KernelExplainer, Explanation
+from obsidian.plotting.shap import partial_dependence, one_shap_value
+
+import numpy as np
+import pandas as pd
+import torch
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+
+
+
+[docs] +class Explainer(): + """ + Base class for surrogate model interpretation and post-hoc analysis. + + Properties: + optimizer (Optimizer): obsidian Optimizer object with fitted surrogate model(s). + + Attributes: + X_space (ParamSpace): obsidian ParamSpace object representing the allowable space for model explanation, + could be different from the optimizer.X_space used for optimization. + responseid (int): the index of a single outcome whose surrogate model will be explained by shap + shap (dict): A dictionary of values containing shapley analysis results + + Raises: + ValueError: If the optimizer is not fit + + """ +
+[docs] + def __init__(self, + optimizer: Optimizer, + X_space: ParamSpace | None = None): + + if not optimizer.is_fit: + raise ValueError('Surrogate model in optimizer is not fit to data. ') + + self.set_optimizer(optimizer) + self.X_space = optimizer.X_space if X_space is None else X_space + + self.shap = {}
+ + + def __repr__(self): + return f"Explainer(optimizer={self.optimizer})" + + @property + def optimizer(self): + """Explainer Optimizer object""" + return self._optimizer + +
+[docs] + def set_optimizer(self, optimizer: Optimizer): + """Sets the explainer optimizer""" + self._optimizer = optimizer
+ + +
+[docs] + def shap_explain(self, + responseid: int = 0, + n: int = 100, + X_ref: pd.DataFrame | None = None, + seed: int | None = None): + """ + Explain the parameter sensitivities using shap values. + + Args: + responseid (int): Index of the target response variable. + n (int): Number of samples to generate for shap values. + X_ref (pd.DataFrame | None): Reference DataFrame for shap values. If None, + the mean of self.X_space will be used. + seed (int | None): Seed value for random number generation. + + Returns: + None: This function fits a Kernel shap explainer and save results as class attributes. + + Raises: + ValueError: If X_ref does not contain all parameters in self.X_space or if X_ref is not a single row DataFrame. + """ + + self.shap['responseid'] = responseid + + if X_ref is None: + X_ref = self.X_space.mean() + else: + if not all(x in X_ref.columns for x in self.X_space.X_names): + raise ValueError('X_ref must contain all parameters in X_space') + if X_ref.shape[0] != 1: + raise ValueError('X_ref must be a single row DataFrame') + + self.shap['X_ref'] = X_ref + + y_name = self.optimizer.target[responseid].name + + def f_preds(X): + # helper function for shap_explain + if isinstance(X, np.ndarray): + X = pd.DataFrame(X, columns=self.X_space.X_names) + for p, x in zip(self.X_space, X.columns): + if isinstance(p, Param_Continuous): + X[x] = pd.to_numeric(X[x]) + y_pred = self.optimizer.predict(X, return_f_inv=True) + mu = y_pred[y_name+' (pred)'].values + return mu + + self.shap['f_preds'] = f_preds + self.shap['explainer'] = KernelExplainer(f_preds, X_ref) + self.shap['X_sample'] = self.X_space.unit_demap( + pd.DataFrame(torch.rand(size=(n, self.X_space.n_dim)).numpy(), + columns=X_ref.columns) + ) + self.shap['values'] = self.shap['explainer'].shap_values(self.shap['X_sample'], seed=seed, l1_reg='aic') + self.shap['explanation'] = Explanation(self.shap['values'], feature_names=X_ref.columns) + + return
+ + +
+[docs] + def shap_summary(self) -> Figure: + """SHAP Summary Plot (Beeswarm)""" + if not self.shap: + raise ValueError('shap explainer is not fit.') + + fig = shap.summary_plot(self.shap['values'], self.shap['X_sample']) + return fig
+ + +
+[docs] + def shap_summary_bar(self) -> Figure: + """SHAP Summary Plot (Bar Plot / Importance)""" + if not self.shap: + raise ValueError('shap explainer is not fit.') + + fig = plt.figure() + shap.plots.bar(self.shap['explanation'], ax=fig.gca(), show=False) + plt.close(fig) + + return fig
+ + +
+[docs] + def shap_pdp_ice(self, + ind=0, # var name or index + ice_color_var=0, # var name or index + ace_opacity: float = 0.5, + ace_linewidth="auto" + ) -> Figure: + """SHAP Partial Dependence Plot with ICE""" + if not self.shap: + raise ValueError('shap explainer is not fit.') + + fig = partial_dependence( + ind=ind, + model=self.f_preds, + data=self.X_sample, + ice_color_var=ice_color_var, + hist=False, + ace_opacity=ace_opacity, + ace_linewidth=ace_linewidth, + show=False + ) + + return fig
+ + +
+[docs] + def shap_single_point(self, + X_new, + X_ref=None): + """SHAP Pair-wise Marginal Explanations""" + if not self.shap: + raise ValueError('shap explainer is not fit.') + + if X_ref is None: + if self.shap_explainer is None: + raise ValueError('shap explainer is not fit. ') + return + shap_value_new = self.shap_explainer.shap_values(X_new) + expected_value = self.shap_explainer.expected_value + else: + # if another reference point is input, need to re-fit another explainer + explainer = shap.KernelExplainer(self.f_preds, X_ref) + shap_value_new = explainer.shap_values(X_new) + expected_value = explainer.expected_value + + shap_value_new = np.squeeze(shap_value_new) + df_shap_value_new = pd.DataFrame([shap_value_new], columns=self.X_space.X_names) + + fig1, fig2 = one_shap_value(shap_value_new, expected_value, self.X_space.X_names) + + return df_shap_value_new, fig1, fig2
+ + +
+[docs] + def cal_sensitivity(self, + dx: float = 1e-6, + X_ref: pd.DataFrame | None = None) -> pd.DataFrame: + """Local parameter sensitivity analysis""" + df_sens = sensitivity(self.optimizer, dx=dx, X_ref=X_ref) + + return df_sens
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/constraints/input.html b/_modules/obsidian/constraints/input.html new file mode 100644 index 0000000..f06505d --- /dev/null +++ b/_modules/obsidian/constraints/input.html @@ -0,0 +1,632 @@ + + + + + + + + + + obsidian.constraints.input — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.constraints.input

+"""Constraints on the input features of a parameter space"""
+
+from obsidian.parameters import ParamSpace, Param_Continuous
+from obsidian.config import TORCH_DTYPE
+
+import torch
+from torch import Tensor
+
+# See  https://botorch.org/api/optim.html, optimize_acqf "equality_constraints", "inequality_constraints",
+# and "nonlinear_inequality_constraints"
+
+# Equality = list(tuple) / tuple = {indices = Tensor, coefficients = Tensor, rhs = float} / sum_i(X[indices[i]] * coefficients[i]) = rhs
+
+# Inquality = list(tuple) / tuple = (indices = Tensor, coefficients = Tensor, rhs = float) / sum_i(X[indices[i]] * coefficients[i]) >= rhs
+# Use 1-d tensor of indices for intra point constraint
+# Use 2-d tensor of indices for inter point constraint where indices[i] = (k_i, l_i, ...)
+
+# Nonlinear inequality = list(tuple) / tuple = (constraint(x) = callable, intra-point = bool)
+# Intra piont: callable takes X in (d) and return scalar
+# Intra point: when q = 1, or when applying the same constraint to each candidate in the batch
+# Inter point: callable takes X in (q x d) and returns scalar
+# Inter point: When q > 1 and the constraints are applied to an entire batch
+
+
+
+[docs] +def InConstraint_Generic(X_space: ParamSpace, + indices: list[float | int] = [0], + coeff: list[float | int | list] = [0], + rhs: int | float = -1) -> tuple[Tensor, Tensor, Tensor]: + """ + Creates an input constraint for a given parameter space. + + Args: + X_space (ParamSpace): The parameter space object. + indices (list[float | int], optional): The indices of the parameters to be constrained. Defaults to ``[0]``. + coeff (list[float | int | list], optional): The coefficients for the parameters in the constraint equation. Defaults to ``[0]``. + rhs (int | float, optional): The right-hand side value of the constraint equation. Defaults to ``-1``. + + Returns: + tuple[Tensor, Tensor, Tensor]: A tuple containing the indices, coefficients, and right-hand side value of the constraint. + + Raises: + TypeError: If X_space is not an obsidian ParamSpace + TypeError: If indices or coeff are not a list + TypeError: If rhs is not numeric + TypeError: If indices are not continuous parameters + + Notes: + - The constraint equation is of the form: coeff_X1 * X1 + coeff_X2 * X2 + ... = rhs + - The indices and coefficients should correspond to continuous parameters in the parameter space. + - The constraint equation is transformed to the encoded space before returning. + + Example: + X_space = ParamSpace(...) + indices = [0, 1] + coeff = [1, -2] + rhs = 10 + constraint = InConstraint_Generic(X_space, indices, coeff, rhs) + """ + + if not isinstance(X_space, ParamSpace): + raise TypeError('X_space must be a ParamSpace object') + if not isinstance(indices, list): + raise TypeError('Indices must be a list') + if not isinstance(coeff, list): + raise TypeError('Coefficients must be a list') + if not isinstance(rhs, (int, float)): + raise TypeError('RHS must be a scalar') + + # Determine which parameters in the decoded space are indicated + i_t = [X_space.tinv_map[i] for i in indices] + + for i in i_t: + if not isinstance(X_space.params[i], Param_Continuous): + raise TypeError('Indeces for input constraints must be \ + for continuous parameters only') + + # We neeed to write the constraints on the encoded space + # SUM(LHS) <= RHS equivalent to -SUM(LHS) >= -RHS + + c_t = [] + rhs_t = rhs + for c, i in zip(coeff, i_t): + param_i = X_space.params[i] + rhs_t -= param_i.min*c + c_t.append(c * param_i.range) + + linear_constraint = (torch.tensor(indices), + torch.tensor([float(c) for c in c_t], dtype=TORCH_DTYPE), + torch.tensor(rhs_t, dtype=TORCH_DTYPE)) + + return linear_constraint
+ + + +
+[docs] +def InConstraint_ConstantDim(X_space: ParamSpace, + dim: int, + tol: int | float = 0.01) -> tuple[callable, bool]: + """ + Constraint which maintains one parameter at a relatively constant (but still freely optimized) + value within a set of suggestions. Useful for simplifying operational complexity of large- + scale experimental optimization (e.g. temperature on a well plate) + + Args: + X_space (ParamSpace): The parameter space object. + dim (int): The dimension of the constant parameter. + tol (int | float, optional): The tolerance value. Defaults to ``0.01``. + + Returns: + tuple[callable, bool]: A tuple containing the constraint function and a boolean indicating if it is an inter-point constraint. + + Raises: + TypeError: If X_space is not an obsidian ParamSpace + TypeError: If dim is not an integer + TypeError: If tol is not numeric + TypeError: If the dimension is not a continuous parameter + + """ + + if not isinstance(X_space, ParamSpace): + raise TypeError('X_space must be a ParamSpace object') + if not isinstance(dim, int): + raise TypeError('Dimension must be an integer') + if not isinstance(tol, (int, float)): + raise TypeError('Tolerance must be a scalar') + if not isinstance(X_space.params[dim], Param_Continuous): + raise TypeError('Constant dimension must be a continuous parameter') + + t_dim = X_space.t_map[dim] + + def nl_func(X: Tensor) -> Tensor: + X_range = X.max(dim=0).values - X.min(dim=0).values + target_range = X_range[t_dim] + return tol - target_range + + nonlinear_constraint = (nl_func, False) # False for inter-point + return nonlinear_constraint
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/constraints/output.html b/_modules/obsidian/constraints/output.html new file mode 100644 index 0000000..3d3da89 --- /dev/null +++ b/_modules/obsidian/constraints/output.html @@ -0,0 +1,546 @@ + + + + + + + + + + obsidian.constraints.output — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.constraints.output

+"""Constraints on the output responses of a model"""
+
+from obsidian.parameters import Target
+from obsidian.utils import unscale_samples
+
+import torch
+from torch import Tensor
+from typing import Callable
+
+# Negative values imply feasibility!
+# Note that these are OUTPUT constraints
+
+
+
+[docs] +def OutConstraint_Blank(target: Target | list[Target]) -> Callable: + """ + Dummy constraint function that proposes all samples as feasible. + + Args: + target (Target or list[Target]): The target or list of targets. + + Returns: + callable: callable constraint function + + """ + def constraint(samples: Tensor) -> Tensor: + samples = unscale_samples(samples, target) + feasibility = -1*torch.ones(size=samples.shape).max(dim=-1).values + return feasibility + return constraint
+ + + +
+[docs] +def OutConstraint_L1(target: Target | list[Target], + offset: int | float = 1) -> Callable: + """ + Calculates the L1 (absolute-value penalized) constraint + + Args: + target (Target | list[Target]): The target value or a list of target values. + offset (int | float, optional): The offset value for the constraint. Defaults to 1. + + Returns: + callable: callable constraint function + + """ + def constraint(samples: Tensor) -> Tensor: + samples = unscale_samples(samples, target) + feasibility = (samples.sum(dim=-1) - offset) + return feasibility + return constraint
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/exceptions.html b/_modules/obsidian/exceptions.html new file mode 100644 index 0000000..4fdda3f --- /dev/null +++ b/_modules/obsidian/exceptions.html @@ -0,0 +1,532 @@ + + + + + + + + + + obsidian.exceptions — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.exceptions

+"""Custom obsidian exceptions for improved error handling"""
+
+
+
+[docs] +class IncompatibleObjectiveError(Exception): + """Exception that gets raised when the objective(s) cannot be successfully called as specified""" + pass
+ + + +
+[docs] +class SurrogateFitError(Exception): + """Exception that gets raised when the surrogate model fails to fit""" + pass
+ + + +
+[docs] +class UnsupportedError(Exception): + """Exception that gets raised when an optimization is requested on an unsupported feature""" + pass
+ + + +
+[docs] +class UnfitError(Exception): + """Exception that gets raised when an action is called before a model or transform has been fit""" + pass
+ + + +
+[docs] +class DataWarning(UserWarning): + """Warning that gets raised if there is an issue with input data""" + pass
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/experiment/benchmark/geometric.html b/_modules/obsidian/experiment/benchmark/geometric.html new file mode 100644 index 0000000..492cdfc --- /dev/null +++ b/_modules/obsidian/experiment/benchmark/geometric.html @@ -0,0 +1,825 @@ + + + + + + + + + + obsidian.experiment.benchmark.geometric — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.experiment.benchmark.geometric

+"""
+Generic simulator functions based on simple geometric surfaces.
+"""
+
+import numpy as np
+import pandas as pd
+import torch
+import math
+
+
+
+[docs] +def paraboloid(X): + """ + Evaluates a simple n-dimensional paraboloid at the specified location. + + Args: + X (ndarray): An (m)-observations by (d)-features array of data to be evaluated. + + Returns: + ndarray: An (m)-sized array of responses. + + """ + + if isinstance(X, pd.DataFrame): + X = X.to_numpy() + + d = X.shape[1] + a = np.ones(d) * 0.5 + z = 100 - (400 / d * (X - a) ** 2).sum(axis=1) + + return z
+ + + +
+[docs] +def shifted_parab(X): + """ + Evaluates a simple n-dimensional parabaloid at the location specified. + Target paraboloid contains a maximum of 100 at all x=0.2 + + Args: + X (ndarray): (m)-observations by (d)-features array of data to be evaluated + + Returns: + ndarray: (m)-sized array of responses + """ + if isinstance(X, pd.DataFrame): + X = X.to_numpy() + + d = X.shape[1] + a = np.ones(d) * 0.2 + z = 100 * np.exp(-(4 / d * (X - a) ** 2).sum(axis=1)) + return z
+ + + +
+[docs] +def cornered_parab(X): + """ + Evaluates a simple n-dimensional parabaloid at the location specified. + + Args: + X (ndarray): (m)-observations by (d)-features array of data to be evaluated + + Returns: + ndarray: (m)-sized array of responses + """ + if isinstance(X, pd.DataFrame): + X = X.to_numpy() + + d = X.shape[1] + a = np.ones(d) * 0 + z = 100 - (400 / d * (X - a) ** 2).sum(axis=1) + + return z
+ + + +
+[docs] +def ackley(X): + """ + Evaluates the N-dimensional Ackley function. + + The Ackley function is a benchmark optimization problem that is commonly used to test optimization algorithms. + It is a multimodal function with multiple local maxima and a global minimum at (0.8, 0.8, ..., 0.8). + This implementation of the Ackley function has been transformed to have a maximum value of 10 at the global minimum. + + Args: + X (ndarray or DataFrame): + An (m x d) array or DataFrame of m observations with d features to be evaluated. + + Returns: + ndarray: + An (m)-sized array of responses. + """ + + if isinstance(X, pd.DataFrame): + X = X.to_numpy() + + # Rescale problem from (0,1) to desired space (-1,3) + X = X*4-3.2 + + a = 10 # default = 20 + b = 0.2 # default = 0.2 + c = 1*math.pi # default = 2*math.pi + d = X.shape[1] + + z = -a*np.exp(-b*((1/d)*np.sum(X**2, axis=1))**0.5) - np.exp((1/d)*np.sum(np.cos(c*X), axis=1))+a+np.exp(1) + + z = -z + 10 + + return z
+ + + +
+[docs] +def rosenbrock(X): + """ + Evaluates the Rosenbrock function in log10 scale, and negative transform (for max). + + The global maximum of the Rosenbrock function is at X = (1, 1, ..., 1). + The maximum value is 10, based on the standard function minimum of 0. We have -log10(0+1e-10). + + Args: + X (ndarray): (m)-observations by (d)-features array of data to be evaluated. + + Returns: + ndarray: (m)-sized array of responses. + """ + + if isinstance(X, pd.DataFrame): + X = X.to_numpy() + + # Rescale problem from (0,1) to desired space (-1,3) + X = X*4-2 + + z = np.zeros(shape=(X.shape[0])) + x_dim = X.shape[1] + + b = 100 + a = 1 + + for i in range(0, x_dim-1): + z += b*(X[:, i+1]-X[:, i]**2)**2+(a-X[:, i])**2 + + z = -np.log10(z+1e-10) + + return z
+ + + +
+[docs] +def sixhump_camel(X): + """ + Evaluates the 6-hump camel function in 2D + Transformed to get a max of 10 at X = (0.0898,-0.7126) and (-0.0898, 0.7126) + + Args: + X (ndarray): (m)-observations by (d)-features array of data to be evaluated. + + Returns: + ndarray: (m)-sized array of responses. + + """ + + if X.shape[1] != 2: + raise ValueError('Camel function only accepts 2 dimensions') + + if isinstance(X, pd.DataFrame): + X = X.to_numpy() + + # Rescale problem from (0,1) to desired space (-2,2),(-1,1) + x1 = X[:, 0]*4-2 + x2 = X[:, 1]*2-1 + + z = (4-2.1*x1**2+(x1**4)/3)*x1**2+x1*x2+(-4+4*x2**2)*x2**2 + + z = -z+1.0316+10 + + return z
+ + + +
+[docs] +def threehump_camel(X): + """ + Evaluates the 3-hump camel function in 2D + Negative transform, to get max + Two global max exist at X = (0,0) + Max is 1 + + Args: + X (ndarray): (m)-observations by (d)-features array of data to be evaluated. + + Returns: + ndarray: (m)-sized array of responses. + + """ + + if isinstance(X, pd.DataFrame): + X = X.to_numpy() + + if X.shape[1] != 2: + raise ValueError('Camel function only accepts 2 dimensions') + + # Rescale problem from (0,1) to desired space (-2,2) + X = X*4-2 + + x1 = X[:, 0] + x2 = X[:, 1] + + z = 2*x1**2-1.05*x1**4+(x1**6)/6+x1*x2+x2**2 + + z = -z+10 + + return z
+ + + +
+[docs] +def perm(X): + """ + Evaluates the perm function in N-dimensions + Negative transform, to get max + Max exists at X = (1,1/2,...,1/d) + Max is 10 + + Args: + X (ndarray): (m)-observations by (d)-features array of data to be evaluated. + + Returns: + ndarray: (m)-sized array of responses. + + """ + + if isinstance(X, pd.DataFrame): + X = X.to_numpy() + + # Rescale problem from (0,1) to desired space (0,1.5) + X = X*2-0.5 + + z = np.zeros(shape=(X.shape[0])) + x_dim = X.shape[1] + + beta = 10 + + for i in range(1, x_dim+1): + inner_sum = 0 + for j in range(1, x_dim+1): + xj = X[:, j-1] + inner_sum += (j+beta)*((xj**i)-(1/j)**i) + z += inner_sum**2 + + z = -np.log(z+1)+10 + + return z
+ + + +
+[docs] +def branin_currin(X): + """ + Synthetic BraninCurrin function from BoTorch test_functions + + Two objective problem composed of the Branin and Currin functions: + + Branin (rescaled): + + f_1(x) = ( + 15*x_1 - 5.1 * (15 * x_0 - 5) ** 2 / (4 * pi ** 2) + 5 * (15 * x_0 - 5) + / pi - 5 + ) ** 2 + (10 - 10 / (8 * pi)) * cos(15 * x_0 - 5)) + + Currin: + + f_2(x) = (1 - exp(-1 / (2 * x_1))) * ( + 2300 * x_0 ** 3 + 1900 * x_0 ** 2 + 2092 * x_0 + 60 + ) / 100 * x_0 ** 3 + 500 * x_0 ** 2 + 4 * x_0 + 20 + + Args: + X (ndarray): (m)-observations by (d=2)-features array of data to be evaluated. + + Returns: + ndarray: (k=2)-sized array of responses. + """ + if isinstance(X, pd.DataFrame): + X = torch.tensor(X.values) + else: + X = torch.tensor(X) + + if X.shape[1] != 2: + raise ValueError('BraninCurrin function only accepts 2 dimensions') + + from botorch.test_functions.multi_objective import BraninCurrin + problem = BraninCurrin(negate=True) + z = problem(X).numpy() + return z
+ + + +
+[docs] +def two_leaves(X): + """ + Simulates the two-leaf multi-output function + + Args: + X (ndarray): (m)-observations by (d=2)-features array of data to be evaluated. + + Returns: + ndarray: (m=2)-sized array of responses. + """ + if isinstance(X, pd.DataFrame): + X = torch.tensor(X.values) + if isinstance(X, np.ndarray) and X.ndim == 1: + X = X[np.newaxis, :] + if isinstance(X, np.ndarray): + X = torch.tensor(X) + x_0 = X[..., 0] + x_1 = X[..., 1] + t = torch.sqrt(torch.abs((x_0-0.4)*math.pi/2)+0.1) + y_0 = t * torch.sin(x_0*math.pi/2) * torch.pow(x_1, 2) + y_1 = t * torch.cos(x_0*math.pi/2) # * torch.pow(x_1,3) + return np.array(torch.stack([y_0, y_1], dim=-1))
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/experiment/benchmark/optithon.html b/_modules/obsidian/experiment/benchmark/optithon.html new file mode 100644 index 0000000..4a715c0 --- /dev/null +++ b/_modules/obsidian/experiment/benchmark/optithon.html @@ -0,0 +1,603 @@ + + + + + + + + + + obsidian.experiment.benchmark.optithon — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.experiment.benchmark.optithon

+"""
+Simulation functions used for an optimization hackathon (OptiThon) in March 2024
+"""
+
+import numpy as np
+import pandas as pd
+from scipy.special import gamma
+
+
+
+[docs] +def Vm_func(X): + # Maximum rate as a function of X + rate = np.exp(-2*(X-0.75)**2) + return rate
+ + + +
+[docs] +def Km_func(X): + # Michaelis constant as a function of X + rate = 2-np.exp(-1*(X-0.4)**2) + return rate
+ + + +
+[docs] +def kI_func(X): + # Inhibition constant as a function of X + rate = 0.4/(1+np.exp(-1*(X-0.2)**2)) + return rate
+ + + +
+[docs] +def response_1(X): + if X.shape[1] != 3: + raise ValueError(f'Input to response_1 must have 3 columns, not {X.shape[1]}') + X1, X2, X3 = X.T + + # Calculate kinetic parameters + Vm = 100*Vm_func(X2) + Km = Km_func(X3) + kI = kI_func(X2) + + # Enzyme inhibition rate law + rate = Vm*X1/(0.001+Km+X1+(X1/kI)**2) + + # Normalize + rate = rate/10.5 + return rate
+ + + +
+[docs] +def response_2(X): + if X.shape[1] != 2: + raise ValueError(f'Input to response_2 must have 2 columns, not {X.shape[1]}') + X1, X2 = X.T + X2 = X2*1 + 2 # (ensure that there is at least one reactor) + + # Gamma function used in RTD calc + gamma_value = gamma(X2) + + # RTD for a pulse into N CSTRs + E_curve = (X1**(X2-1))*((2*X2)**X2)*np.exp(-2*X2*X1)/(gamma_value) + + # Normalize + E_curve = E_curve/1.62 + + return E_curve
+ + + +
+[docs] +def OT_simulator(X, addNoise=False): + if isinstance(X, pd.DataFrame): + X = X.to_numpy() + if isinstance(X, np.ndarray) and X.ndim == 1: + X = X[np.newaxis, :] + + if X.shape[1] != 6: + raise ValueError(f'Input to simulate must have 6 columns, not {X.shape[1]}') + + # Break down the data into 3 parts for separate functions + X123 = X[:, 0:3] + X45 = X[:, 3:5] + X6 = X[:, 5] + + # First three cols represented in an enzyme kinetics problem + y1 = response_1(X123) + + # Next two cols represented in a flow problem + y2 = response_2(X45) + + # Total performance is a weighted sum of two problems + y = 0.6*y1 + 0.4*y2 + + # Final column has a mean 0 effect, but determines the scale of noise + # Note: The overall problem is slightly heteroskedastic + if addNoise: + sd = 0.01+0.02*X6 + rel_error = np.random.normal(loc=1, scale=sd, size=y.shape[0]) + y *= rel_error + + return y
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/experiment/design.html b/_modules/obsidian/experiment/design.html new file mode 100644 index 0000000..3b9fac9 --- /dev/null +++ b/_modules/obsidian/experiment/design.html @@ -0,0 +1,625 @@ + + + + + + + + + + obsidian.experiment.design — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.experiment.design

+"""Design initial experiments"""
+
+from .utils import factorial_DOE
+
+from obsidian.parameters import ParamSpace
+from obsidian.exceptions import UnsupportedError
+
+from botorch.utils.sampling import draw_sobol_samples
+from numpy.typing import ArrayLike
+from scipy.stats import qmc
+
+import torch
+from torch import Tensor
+import pandas as pd
+import warnings
+
+
+
+[docs] +class ExpDesigner: + """ + ExpDesigner is a base class for designing experiments in a parameter space. + + Attributes: + X_space (ParamSpace): The parameter space for the experiment. + seed (int | None): The randomization seed. + + Raises: + TypeError: If X_space is not an obsidian ParamSpace object. + """ + +
+[docs] + def __init__(self, + X_space: ParamSpace, + seed: int | None = None): + if not isinstance(X_space, ParamSpace): + raise TypeError('X_space must be an obsidian ParamSpace object') + + self.X_space = X_space + self.seed = seed
+ + + def __repr__(self): + """String representation of object""" + return f"obsidian ExpDesigner(X_space={self.X_space})" + +
+[docs] + def initialize(self, + m_initial: int | None = None, + method: str = 'LHS', + sample_custom: Tensor | ArrayLike | None = None) -> pd.DataFrame: + """ + Initializes the experiment design. + + Args: + m_initial (int): The number of experiments to initialize. + method (str, optional): The method to use for initialization. Defaults to ``'LHS'``. + seed (int | None, optional): The randomization seed. Defaults to ``None``. + sample_custom (Tensor | ArrayLike | None, optional): Custom samples for initialization. Defaults to ``None``. + + Returns: + pd.DataFrame: The initialized experiment design. + + Raises: + KeyError: If method is not one of the supported methods. + ValueError: If sample_custom is None when method is 'Custom'. + ValueError: If the number of columns in sample_custom does not match the size of the feature space. + """ + d = self.X_space.n_dim + + if m_initial is None: + m_initial = int(d*2) + m = m_initial + seed = self.seed + + method_dict = { + 'LHS': lambda d, m: torch.tensor( + qmc.LatinHypercube(d=d, scramble=False, seed=seed, strength=1, optimization='random-cd').random(n=m)), + 'Random': lambda d, m: torch.rand(size=(m, d)), + 'Sobol': lambda d, m: draw_sobol_samples( + bounds=torch.tensor([0.0, 1.0]).reshape(2, 1).repeat(1, d), n=m, q=1).squeeze(1), + 'Custom': lambda d, m: torch.tensor(sample_custom), + 'DOE_full': lambda d, m: torch.tensor(factorial_DOE(d=d, n_CP=3, shuffle=True, seed=seed, full=True)), + 'DOE_res4': lambda d, m: torch.tensor(factorial_DOE(d=d, n_CP=3, shuffle=True, seed=seed)) + } + + if method not in method_dict.keys(): + raise KeyError(f'Method must be one of {method_dict.keys()}') + if method == 'Custom': + if sample_custom is None: + raise ValueError('Must provide samples for custom') + if method in ['DOE_full', 'DOE_res4']: + if self.X_space.X_discrete: + raise UnsupportedError('DOE methods not currently designed for discrete parameters') + + if seed is not None: + torch.manual_seed(seed) + if not torch.cuda.is_available(): + torch.use_deterministic_algorithms(True) + + if sample_custom is not None: + if sample_custom.shape[1] != d: + raise ValueError('Columns in custom sample do not match size of feature space') + + # Generate [0-1) samples for each parameter + sample = method_dict[method](d, m) + + m_required = sample.shape[0] + + if m_required > m: + warnings.warn(f'The number of experiments required to initialize the requested design \ + ({m_required}) exceeds the m_initial specified ({m}). \ + Proceeding with larger number of experiments.') + elif m_required < m: + print(f'The number of initialization experiments ({m}) exceeds the required \ + number for the requested design ({m_required}). Filling with randomized experiments.') + excess = m - m_required + sample_add = torch.rand(size=(excess, d)) + sample = torch.vstack((sample, sample_add)) + + sample = pd.DataFrame(sample.numpy(), columns=self.X_space.X_names) + + # Reset parameters to 0 which are not allowed to vary in X_space + for param in self.X_space.X_static: + sample[param] = 0 + + # Map samples into parameter space + X_0 = self.X_space.unit_demap(sample) + + return X_0
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/experiment/simulator.html b/_modules/obsidian/experiment/simulator.html new file mode 100644 index 0000000..35f6325 --- /dev/null +++ b/_modules/obsidian/experiment/simulator.html @@ -0,0 +1,597 @@ + + + + + + + + + + obsidian.experiment.simulator — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.experiment.simulator

+"""Simulate virtual experimental data"""
+
+from obsidian.parameters import ParamSpace
+
+from typing import Callable
+import pandas as pd
+import numpy as np
+import warnings
+
+
+
+[docs] +class Simulator: + """ + Simulator class for generating in-silico responses to requested experiments. + + This class provides functionality to simulate responses to a set of experiments based on a given response function. + The simulated responses can be subject to error, which is controlled by the `eps` parameter. + + Attributes: + X_space (ParamSpace): The ParamSpace object representing the allowable space for optimization. + response_function (Callable): The callable function used to convert experiments to responses. + name (str or list[str]): Name of the simulated output(s). Default is ``Response``. + eps (float or list[float]): The simulated error to apply, as the standard deviation of the Standard + Normal distribution. Default is ``0``. + kwargs (dict): Optional hyperparameters for the response function. + + Raises: + TypeError: If response_function is not a callable function. + TypeError: If X_space is not an obsidian ParamSpace object. + + """ + +
+[docs] + def __init__(self, + X_space: ParamSpace, + response_function: Callable, + name: str | list[str] = 'Response', + eps: float | list[float] = 0.0, + **kwargs): + + if not callable(response_function): + raise TypeError('Response generator must be a callable function') + if not isinstance(X_space, ParamSpace): + raise TypeError('X_space must be an obsidian ParamSpace object') + + self.X_space = X_space + self.response_function = response_function + self.name = name + self.eps = eps if isinstance(eps, list) else [eps] + self.kwargs = kwargs
+ + + def __repr__(self): + """String representation of object""" + return f" obsidian Simulator(response_function={self.response_function.__name__}, eps={self.eps})" + +
+[docs] + def simulate(self, + X_prop: pd.DataFrame) -> np.ndarray: + """ + Generates a response to a set of experiments. + + Currently, response function only handles strictly numeric values and categories are manually penalized. + + Args: + X_prop (pd.DataFrame): Proposed experiments to evaluate. + + + Returns: + np.ndarray: Array of response values to experiments. + """ + # De-map everything into 0,1 based on ranges + X = self.X_space.unit_map(X_prop).values + + y_sim = self.response_function(X) + + # Apply error + # Expand length of eps to match number of outputs + if len(self.eps) == 1: + self.eps *= y_sim.ndim + if y_sim.ndim == 1: + y_sim = y_sim.reshape(-1, 1) + for i in range(y_sim.shape[1]): + rel_error = np.random.normal(loc=1, scale=self.eps[i], size=y_sim.shape[0]) + y_sim[:, i] *= rel_error + + # Handle naming conventions + y_dims = y_sim.shape[1] + if isinstance(self.name, list): + if len(self.name) != y_dims: + warnings.warn("Number of names does not match the number of dimensions. Using default response names.") + self.name = [f'{self.name} {i+1}' for i in range(y_dims)] + else: + if y_dims == 1: + self.name = [self.name] + else: + self.name = [f'{self.name} {i+1}' for i in range(y_dims)] + + df_sim = pd.DataFrame(y_sim, columns=self.name) + + return df_sim
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/experiment/utils.html b/_modules/obsidian/experiment/utils.html new file mode 100644 index 0000000..74cb674 --- /dev/null +++ b/_modules/obsidian/experiment/utils.html @@ -0,0 +1,576 @@ + + + + + + + + + + obsidian.experiment.utils — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.experiment.utils

+
+from obsidian.exceptions import UnsupportedError
+
+import numpy as np
+from itertools import product
+
+
+
+[docs] +def factorial_DOE(d: int, + n_CP: int = 3, + shuffle: bool = True, + seed: int | None = None, + full: bool = False): + """ + Creates a statistically designed factorial experiment (DOE). + Specifically for 2-level designs only. + Uses the range (0,1) for low-high instead of the typical (-1,1), + although (-1,1) is used for calculations during alias design + + Args: + d (int): Number of dimensions/inputs in the design. + n_CP (int, optional): The number of centerpoints to include in the design, for estimating + uncertainty and curvature. Default is ``3``. + shuffle (bool, optional): Whether or not to shuffle the design or leave them in the default run + order. Default is ``True``. + seed (int, optional): Randomization seed. Default is ``None``. + full (bool, optional): Whether or not to run the full DOE. Default is ``False``, which + will lead to an efficient Res4+ design. + + Returns: + ndarray: An (m)-by-(d) array of experiments in the (0,1) domain + + Raises: + UnsupportedError: If the number of dimensions exceeds 12 + """ + if d > 12: + raise UnsupportedError('The number of dimensions must be 12 or fewer for DOE (currently)') + + steps = np.array([-1, 1]) + CP = np.zeros(shape=(n_CP, d)) + + # Create a dictionary that allows us to use letters + term_codes = {} + alphabet = 'ABCDEFGHJKLMNOPQRSTUVWXYZ' # DESIGN EXPERT SKIPS I!!! + for i, letter in enumerate(alphabet): + term_codes[letter] = i + + res4_aliases = {2: {}, 3: {}, + 4: {'D': 'ABC'}, + 5: {'E': 'ABCD'}, 6: {'E': 'ABC', 'F': 'BCD'}, + 7: {'F': 'ABCD', 'G': 'ABCE'}, 8: {'F': 'ABC', 'G': 'ABD', 'H': 'BCDE'}, + 9: {'G': 'ABCD', 'H': 'ACEF', 'J': 'CDEF'}, + 10: {'G': 'ABCD', 'H': 'ABCE', 'J': 'ADEF', 'K': 'BDEF'}, + 11: {'G': 'ABCD', 'H': 'ABCE', 'J': 'ABDE', 'K': 'ACDEF', 'L': 'BCDEF'}, + 12: {'H': 'ABC', 'J': 'ADEF', 'K': 'BDEG', 'L': 'CDFG', 'M': 'ABCEFG'} + } + + if full: + axes = np.tile(steps, (d, 1)) + X = np.array(list(product(*axes))) + else: + if res4_aliases[d] == {}: + d_resolved = d + else: + decoded_keys = [term_codes[k] for k in res4_aliases[d].keys()] + d_resolved = np.min(decoded_keys) # e.g. If the first aliased key is G, we are resolved up to F + axes = np.tile(steps, (d_resolved, 1)) + X = np.array(list(product(*axes))) + for term, generator in res4_aliases[d].items(): + decoded_generator = [term_codes[g] for g in list(generator)] + aliased_term = np.product(X[:, decoded_generator], axis=-1)[:, np.newaxis] + X = np.hstack((X, aliased_term)) + + # Add centerpoints then shuffle + X = np.vstack((X, CP)) + if seed is not None: + np.random.seed(seed) + if shuffle: + np.random.shuffle(X) + # Rescale from (-1,1) to (0,0.999999) + X = (X+1)/2 - 1e-6 + + return X
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/objectives/base.html b/_modules/obsidian/objectives/base.html new file mode 100644 index 0000000..7cf6fd9 --- /dev/null +++ b/_modules/obsidian/objectives/base.html @@ -0,0 +1,559 @@ + + + + + + + + + + obsidian.objectives.base — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.objectives.base

+
+"""Base class for obsidian objective functions"""
+
+from obsidian.utils import tensordict_to_dict
+
+from botorch.acquisition.multi_objective.objective import MCAcquisitionObjective, MCMultiOutputObjective
+from abc import abstractmethod
+
+from torch import Tensor
+
+
+
+[docs] +class Objective(MCMultiOutputObjective, MCAcquisitionObjective): + """ + An objective function which is calculated from the model output(s) and input(s). + + Attributes: + _is_mo (bool): A flag indicating whether the final output is multi-output. + + """ + + def __init__(self, + mo: bool = False) -> None: + + super().__init__() + self._is_mo = mo + +
+[docs] + def output_shape(self, + samples: Tensor) -> Tensor: + """Converts the output to the correct Torch shape based on the multi-output flag""" + return samples.squeeze(-1) if not self._is_mo else samples
+ + +
+[docs] + @abstractmethod + def forward(self, + samples: Tensor, + X: Tensor | None = None) -> Tensor: + + pass # pragma: no cover
+ + +
+[docs] + def save_state(self) -> dict: + """Saves the objective to a state dictionary""" + obj_dict = {'name': self.__class__.__name__, + 'state_dict': tensordict_to_dict(self.state_dict())} + + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, obj_dict: dict): + """Loads the objective from a state dictionary""" + return cls(**obj_dict['state_dict'])
+ + + def __repr__(self): + """String representation of object""" + return f'{self.__class__.__name__} (mo={self._is_mo})'
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/objectives/custom.html b/_modules/obsidian/objectives/custom.html new file mode 100644 index 0000000..1678bcf --- /dev/null +++ b/_modules/obsidian/objectives/custom.html @@ -0,0 +1,840 @@ + + + + + + + + + + obsidian.objectives.custom — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.objectives.custom

+"""Custom objective function formulations"""
+
+from .base import Objective
+
+from obsidian.parameters import ParamSpace, Param_Continuous, Target
+from obsidian.utils import tensordict_to_dict
+from obsidian.config import TORCH_DTYPE
+
+import torch
+from torch import Tensor
+
+
+
+[docs] +class Identity_Objective(Objective): + """ + Dummy multi-output objective function that simply returns the input Y. + """ + def __init__(self, + mo: bool = False) -> None: + super().__init__(mo) + +
+[docs] + def forward(self, + samples: Tensor, + X: Tensor | None = None) -> Tensor: + """ + Evaluate the objective function on the candidate set samples, X + """ + return self.output_shape(samples)
+
+ + + +
+[docs] +class Feature_Objective(Objective): + """ + Creates an objective function which creates a new outcome as a linear combination of input features. + + Always a multi-output objective. + + Args: + X_space (ParamSpace): The parameter space. + indices (list[float | int], optional): The indices of the parameters in the real space + to be used as features. Defaults to ``[0]``. + coeff (list[float | int | list], optional): The coefficients corresponding to each feature. + Defaults to ``[0]``. + + Raises: + ValueError: If the length of `indices` and `coeff` are not the same. + TypeError: If the indices for input objectives are not for continuous parameters. + """ + + def __init__(self, + X_space: ParamSpace, + indices: list[float | int], + coeff: list[float | int | list]) -> None: + + super().__init__(mo=True) + + if len(indices) != len(coeff): + raise ValueError("featureids and coeff must have the same length") + + for i in indices: + if not isinstance(X_space.params[i], Param_Continuous): + raise TypeError('Indices for input objectives must be for continuous parameters only') + + self.register_buffer('coeff', torch.tensor(coeff, dtype=TORCH_DTYPE)) + self.register_buffer('indices', torch.tensor(indices, dtype=torch.long)) + self.X_space = X_space + + def __repr__(self): + """String representation of object""" + return f'{self.__class__.__name__} (indices={self.indices.tolist()}, coeff={self.coeff.tolist()})' + +
+[docs] + def forward(self, + samples: Tensor, + X: Tensor) -> Tensor: + """ + Evaluate the objective function on the candidate set samples, X + """ + # Ydim = s * b * q * m + # Xdim = b * q * d + # if q is 1, it is omitted + + X_u_all = [] # Create unscaled X + for i in self.indices: + X_u_all.append(torch.tensor( + self.X_space[i].decode(X[..., i].detach().numpy()), + dtype=TORCH_DTYPE).unsqueeze(-1)) # Add output dimension + + X_u = torch.concat(X_u_all, dim=-1).unsqueeze(0) # Add sample dimension to X_u + cX = self.coeff * X_u + + # Make sure that the new obejctive matches Y in shape + # Sum the C*X features as a new output + feature_obj = cX.sum(dim=-1, keepdim=True).repeat_interleave(samples.shape[0], 0) + + total_obj = torch.concat([samples, feature_obj], dim=-1) + + return total_obj
+ + +
+[docs] + def save_state(self) -> dict: + """Saves the objective to a state dictionary""" + obj_dict = {'name': self.__class__.__name__, + 'state_dict': tensordict_to_dict(self.state_dict()), + 'X_space': self.X_space.save_state()} + + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, obj_dict: dict): + """Loads the objective from a state dictionary""" + new_obj = cls(ParamSpace.load_state(obj_dict['X_space']), + **obj_dict['state_dict']) + + return new_obj
+
+ + + +
+[docs] +class Utopian_Distance(Objective): + """ + A custom objective for calculating the distance between an output response + and a most-desirable, ideal/utopian point. + + Args: + utopian (list[float]): A list of values representing the utopian point. + targets (Target | list[Target]): A single Target object or a list of Target objects. + + Attributes: + u_t (Tensor): The utopian coordinates in transformed space + + Raises: + TypeError: If the input is not a list of Target objects. + ValueError: If the length of the utopian point and targets are not the same. + + """ + + def __init__(self, + utopian: list[float], + targets: Target | list[Target]) -> None: + + if not isinstance(targets, list): + targets = [targets] + if not all(isinstance(t, Target) for t in targets): + raise TypeError('All elements in the list must be Target objects') + + if len(utopian) != len(targets): + raise ValueError("Length of utopian and targets must be the same") + + super().__init__(mo=len(targets) > 1) + + self.register_buffer('utopian', torch.tensor(utopian, dtype=TORCH_DTYPE)) + self.targets = targets + + # Must transform the utopian coordinates into the transformed space where + # the optimizer operates on target samples + u_t = [t.transform_f(u) for u, t in zip(utopian, targets)] + self.u_t = torch.tensor(u_t, dtype=TORCH_DTYPE).flatten() + +
+[docs] + def forward(self, + samples: Tensor, + X: Tensor | None = None) -> Tensor: + """ + Evaluate the objective function on the candidate set samples, X + """ + distance = (-1)*(self.u_t-samples).abs() + + return self.output_shape(distance)
+ + + def __repr__(self): + """String representation of object""" + return f'{self.__class__.__name__} (utopian={self.utopian.tolist()})' + +
+[docs] + def save_state(self) -> dict: + """Saves the objective to a state dictionary""" + obj_dict = {'name': self.__class__.__name__, + 'state_dict': tensordict_to_dict(self.state_dict()), + 'targets': [t.save_state() for t in self.targets]} + + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, obj_dict: dict): + """Loads the objective from a state dictionary""" + new_obj = cls(targets=[Target.load_state(t_dict) for t_dict in obj_dict['targets']], + **obj_dict['state_dict']) + + return new_obj
+
+ + + +
+[docs] +class Bounded_Target(Objective): + """ + Represents a bounded target objective for multi-output and single-output + acquisition objectives + + Args: + bounds (list[tuple[float, float] | None]): The bounds for each target. + If a bound is None, it is ignored. + targets (Target | list[Target]): The target or list of targets. + tau (float, optional): The temperature parameter for the sigmoid function. + Defaults to ``1e-3``. + + Attributes: + lb (torch.Tensor): The lower bounds for the targets. + ub (torch.Tensor): The upper bounds for the targets. + tau (torch.Tensor): The temperature parameter for the sigmoid function. + indices (torch.Tensor): The indices of the non-None bounds. + + """ + + def __init__(self, + bounds: list[tuple[float, float] | None], + targets: Target | list[Target], + tau: float = 1e-3) -> None: + if not isinstance(targets, list): + targets = [targets] + + if not all(isinstance(t, Target) for t in targets): + raise TypeError('All elements in the list must be Target objects') + + self.targets = targets + + if len(bounds) != len(targets): + raise ValueError("Length of bounds and targets must be the same") + + super().__init__(mo=len(targets) > 1) + + # Only store and process the indicated bounds + indices = [] + lb_t = [] + ub_t = [] + for i, (b, target) in enumerate(zip(bounds, targets)): + if b is not None: + lb_t.append(target.transform_f(b[0]).iloc[0]) + ub_t.append(target.transform_f(b[1]).iloc[0]) + indices.append(i) + + self.bounds = bounds + self.indices = torch.tensor(indices) + self.lb = torch.tensor(lb_t, dtype=TORCH_DTYPE).flatten() + self.ub = torch.tensor(ub_t, dtype=TORCH_DTYPE).flatten() + tau = torch.tensor(tau, dtype=TORCH_DTYPE) + + self.register_buffer('tau', tau) + + def __repr__(self): + """String representation of object""" + return f'{self.__class__.__name__} (indices={self.indices.tolist()}, bounds={self.bounds})' + +
+[docs] + def forward(self, + samples: Tensor, + X: Tensor | None = None) -> Tensor: + """ + Evaluate the objective function on the candidate set samples, X + """ + approx_lb = torch.sigmoid((samples[..., self.indices] - self.lb) / self.tau) + approx_ub = torch.sigmoid((self.ub - samples[..., self.indices]) / self.tau) + product = approx_lb * approx_ub + out = samples.to(TORCH_DTYPE) + out[..., self.indices] = product + return self.output_shape(out)
+ + +
+[docs] + def save_state(self) -> dict: + """Saves the objective to a state dictionary""" + obj_dict = {'name': self.__class__.__name__, + 'state_dict': tensordict_to_dict(self.state_dict()), + 'bounds': self.bounds, + 'targets': [t.save_state() for t in self.targets]} + + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, obj_dict: dict): + """Loads the objective from a state dictionary""" + new_obj = cls(targets=[Target.load_state(t_dict) for t_dict in obj_dict['targets']], + bounds=obj_dict['bounds'], + **obj_dict['state_dict']) + + return new_obj
+
+ + + +
+[docs] +class Index_Objective(Objective): + """ + Creates an objective function that returns the single-objective output + from a multi-output model, at the specified index. + + Always a single-output objective. + + Args: + index (int): The index of the value to be returned. + """ + def __init__(self, + index: int = 0) -> None: + super().__init__(mo=False) + self.register_buffer('index', torch.tensor(index)) + + def __repr__(self): + """String representation of object""" + return f'{self.__class__.__name__} (index={self.index.item()})' + +
+[docs] + def forward(self, + samples: Tensor, + X: Tensor | None = None) -> Tensor: + """ + Evaluate the objective function on the candidate set samples, X + """ + return samples[..., self.index]
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/objectives/scalarize.html b/_modules/obsidian/objectives/scalarize.html new file mode 100644 index 0000000..252a597 --- /dev/null +++ b/_modules/obsidian/objectives/scalarize.html @@ -0,0 +1,628 @@ + + + + + + + + + + obsidian.objectives.scalarize — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.objectives.scalarize

+"""Scalarization methods for reducing multi-output to single-output objectives"""
+
+from .base import Objective
+
+from obsidian.config import TORCH_DTYPE
+
+import torch
+from torch import Tensor
+
+# Reference: https://arxiv.org/pdf/1904.05760
+
+
+
+[docs] +class Scalarization(Objective): + """ + Base scalarization objective, which condenses multiple outputs into a single one + + Always a single-output objective. + """ + + def __init__(self) -> None: + super().__init__(mo=False) + + def __repr__(self): + """String representation of object""" + return f'{self.__class__.__name__} (weights={self.weights.tolist()})'
+ + + +
+[docs] +class Scalar_WeightedSum(Scalarization): + """ + Scalarizes a multi-output response using a weighted sum + + Args: + weights (list[float]): A list of weights to be applied to the response tensor. + + """ + def __init__(self, + weights: list[float]): + super().__init__() + self.register_buffer('weights', torch.tensor(weights, dtype=TORCH_DTYPE)) + +
+[docs] + def forward(self, + samples: Tensor, + X: Tensor | None = None) -> Tensor: + """ + Evaluate the objective function on the candidate set samples, X + """ + return (self.weights * samples).sum(dim=-1)
+
+ + + +
+[docs] +class Scalar_WeightedNorm(Scalarization): + """ + Scalarizes a multi-output response using a weighted norm + + Args: + weights (list[float]): A list of weights to be applied to the response tensor. + norm (int or None, optional): The order of vector norm to be used. If None + is provided, the p-norm will be used + neg (bool, optional): Whether or not to return the negative norm, which is + required for maximizing norms based on distance to a target (utopian point). + + """ + def __init__(self, + weights: list[float], + norm: int | None = None, + neg: bool = False): + super().__init__() + self.register_buffer('weights', torch.tensor(weights, dtype=TORCH_DTYPE)) + + if norm is None: + norm = len(weights) + self.register_buffer('norm', torch.tensor(norm, dtype=torch.long)) + + self.register_buffer('neg', torch.tensor(neg, dtype=torch.bool)) + self.C = -1 if neg else 1 + +
+[docs] + def forward(self, + samples: Tensor, + X: Tensor | None = None) -> Tensor: + """ + Evaluate the objective function on the candidate set samples, X + """ + return self.C*(self.weights * samples).norm(self.norm, dim=-1)
+
+ + + +
+[docs] +class Scalar_Chebyshev(Scalarization): + """ + Scalarizes a multi-output response using the augmented Chebyshev function. + + The augmented Chebyshev function maximizes the minimum scaled-response in conjunction with a weighted sum. + + Args: + weights (list[float]): A list of weights to be applied to the response tensor. + alpha (float, optional): The scaling factor for the weighted sum. Defaults to ``0.05``. + augment (bool, optional): Flag indicating whether to perform augmentation. Defaults to ``True``. + + """ + def __init__(self, + weights: list[float], + alpha: float = 0.05, + augment: bool = True): + super().__init__() + self.register_buffer('weights', torch.tensor(weights, dtype=TORCH_DTYPE)) + self.register_buffer('alpha', torch.tensor(alpha, dtype=TORCH_DTYPE)) + self.register_buffer('augment', torch.tensor(augment, dtype=torch.bool)) + +
+[docs] + def forward(self, + samples: Tensor, + X: Tensor | None = None) -> Tensor: + """ + Evaluate the objective function on the candidate set samples, X + """ + # Augmentation seeks to maximize weighted sum + delta = self.alpha * (self.weights * samples).sum(dim=-1) if self.augment else 0 + + # Maximize the smallest weighted outcome + return (-1 * self.weights * samples).max(dim=-1).values + delta
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/objectives/sequence.html b/_modules/obsidian/objectives/sequence.html new file mode 100644 index 0000000..138db2d --- /dev/null +++ b/_modules/obsidian/objectives/sequence.html @@ -0,0 +1,570 @@ + + + + + + + + + + obsidian.objectives.sequence — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.objectives.sequence

+"""Wrapper class for combining multiple objectives into a single objective."""
+
+from .base import Objective
+from .config import obj_class_dict
+
+from torch import Tensor
+
+
+
+[docs] +class Objective_Sequence(Objective): + """ + A composite objective that combines multiple acquisition + objectives into a single objective, potentially single or multi-output + + Attributes: + obj_list (list[Objective]): The list of acquisition objectives. + + Raises: + TypeError: If the input is not a list of Objective objects. + """ + + def __init__(self, + obj_list: list[Objective]) -> None: + if not isinstance(obj_list, list): + raise TypeError('Objective_Sequence input must be a list of objectives') + if not all(isinstance(obj, Objective) for obj in obj_list): + raise TypeError('All elements in the list must be Objective objects') + + super().__init__(mo=obj_list[-1]._is_mo) + + self.obj_list = obj_list + + # None but the last objective can squeeze the multi-output dimension (if at all) + for obj in obj_list[:-1]: + obj._is_mo = True + + def __repr__(self): + """String representation of object""" + return f'{self.__class__.__name__} (obj_list={self.obj_list})' + +
+[docs] + def forward(self, + samples: Tensor, + X: Tensor | None = None) -> Tensor: + """ + Evaluate the objective function(s) on the candidate set samples, X + """ + for obj in self.obj_list: + samples = obj.forward(samples, X=X) + + return samples
+ + +
+[docs] + def save_state(self) -> dict: + """Saves the objective(s) to a state dictionary""" + obj_dict = {'name': self.__class__.__name__, + 'obj_list': [obj.save_state() for obj in self.obj_list]} + + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, obj_dict: dict): + """Loads the objective(s) from a state dictionary""" + new_obj_list = [] + + for obj_dict_i in obj_dict['obj_list']: + obj_class = obj_class_dict[obj_dict_i['name']] + new_obj_list.append(obj_class.load_state(obj_dict_i)) + + return cls(new_obj_list)
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/optimizer/base.html b/_modules/obsidian/optimizer/base.html new file mode 100644 index 0000000..b2fae42 --- /dev/null +++ b/_modules/obsidian/optimizer/base.html @@ -0,0 +1,768 @@ + + + + + + + + + + obsidian.optimizer.base — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.optimizer.base

+"""Optimizer class definition"""
+
+from obsidian.parameters import ParamSpace, Target, Param_Observational
+from obsidian.exceptions import UnsupportedError
+
+from botorch.utils.multi_objective.box_decompositions.dominated import DominatedPartitioning
+from botorch.utils.multi_objective.pareto import is_non_dominated
+from abc import ABC, abstractmethod
+
+import pandas as pd
+import numpy as np
+import torch
+import random
+from torch import Tensor
+
+
+
+[docs] +class Optimizer(ABC): + """ + Base class for obsidian optimizer, which fits a surrogate model to data and suggests + optimal experiments + + Attributes: + X_space (ParamSpace): obsidian ParamSpace object representing the allowable space for optimization. + seed (int | None): Randomization seed for the optimizer and stochastic surrogate models. + verbose (int): Flag for monitoring and debugging optimization output. + + Raises: + ValueError: If verbose is not set to 0, 1, 2, or 3. + TypeError: If X_space is not an obsidian ParamSpace + """ + +
+[docs] + def __init__(self, + X_space: ParamSpace, + seed: int | None = None, + verbose: int = 1): + + # Verbose selection + if verbose not in [0, 1, 2, 3]: + raise ValueError('Verbose option must be 0 (no output), 1 (summary output), \ + 2 (detailed output), or 3 (debugging)') + self.verbose = verbose + + # Handle randomization seed, considering all 3 sources (torch, random, numpy) + self.seed = seed + if self.seed is not None: + torch.manual_seed(self.seed) + if not torch.cuda.is_available(): + torch.use_deterministic_algorithms(True) + np.random.seed(self.seed) + random.seed(self.seed) + + # Store the parameter space which contains useful reference properties + if not isinstance(X_space, ParamSpace): + raise TypeError('X_space must be an obsidian ParamSpace object') + self.X_space = X_space
+ + + def _fixed_features(self, + fixed_var: dict | None = None) -> list: + """ + Returns a list of fixed features for optimization. + + Args: + fixed_var (dict): A dictionary of fixed variables and their corresponding settings. + The dictionary should be in the format: {param_name: param_setting}. + + Returns: + list: A list of fixed features for optimization. + + Raises: + TypeError: If fixed_var is not a dictionary. + NameError: If a parameter in fixed_var is not found in X_space. + """ + + # Set static optimization features (used for fitting, but set to a fixed number during optimization) + # e.g. Optimizing for max yield at a fixed end-time in a reaction + + fixed_var = {} if fixed_var is None else fixed_var + + fixed_features_list = [] + + # Add any observational variables to fixed_var + if self.X_space.X_obs: + fixed_obs = {p.name: p.design_point for p in self.X_space if isinstance(p, Param_Observational)} + fixed_var.update(fixed_obs) + + # Store each validate setting list in a dataframe, then merge by cartesian product to get all valid combos + df_list = [] + + if fixed_var: + if not isinstance(fixed_var, dict): + raise TypeError('Fixed variables must be provided as a dict: {param_name:param_setting}') + for var, level in fixed_var.items(): + if var not in self.X_space.X_names: + raise NameError(f'Profile variable {var} not found in {self.X_space.X_names}') + param_i = next(param for param in self.X_space if param.name == var) + param_i._validate_value(level) + df_i = pd.DataFrame({var: [level]}) + df_list.append(df_i) + + # If we have discrete parameters, we need to limit the acquisition function search as such + # First, get the cartesian product of all of the categorical/ordinal combos + for x in self.X_space.X_discrete: + if x.name not in fixed_var.keys(): # Fixed_var should take precedent and lock out other combinations + df_i = pd.DataFrame({x.name: x.categories}) + df_list.append(df_i) + + # Merge by cross + if df_list: + df_all = df_list[0] + for df_i in df_list[1:]: + df_all = pd.merge(df_all, df_i, how='cross') + encoded_cross = self.X_space.encode(df_all) + + # Fixed feature list requires names to be the indeces of our columns + X_t_names = self.X_t_train.columns + encoded_cross = encoded_cross.rename(columns={col: X_t_names.get_loc(col) for col in encoded_cross.columns}) + fixed_features_list += encoded_cross.to_dict(orient='records') + + return fixed_features_list + +
+[docs] + def hypervolume(self, + f: Tensor, + ref_point: list | None = None, + weights: list | None = None, + ) -> float: + """ + Calculates the hypervolume of the given data points. + + Args: + f (Tensor): The data points to calculate the hypervolume for. + ref_point (list, optional): The reference point for the hypervolume calculation. Defaults to ``None``. + weights (list, optional): The weights to apply to each objective. Defaults to ``None``. + + Returns: + float: The hypervolume value. + + Raises: + UnsupportedError: If the number of objectives is less than or equal to 1. + """ + + if f.shape[1] <= 1: + raise UnsupportedError('Cannot calculate hypervolume for single objective') + if f.ndim != 2: + raise ValueError('Hypervolume calculation only supported for 2D tensor') + + if ref_point is None: + ref_point = f.min(dim=0).values + else: + if len(ref_point) != f.shape[1]: + raise ValueError('Reference point must have the same number of objectives as the data') + # Need to transform ref_point if provided + ref_point = torch.tensor(ref_point) + + if weights is None: + weights = torch.ones(size=(f.shape[1],)) + else: + if len(weights) != f.shape[1]: + raise ValueError('Weights must have the same number of objectives as the data') + weights = torch.tensor(weights) + + f_weighted = f * weights + bd = DominatedPartitioning(ref_point=ref_point, Y=f_weighted) + hv = bd.compute_hypervolume().item() + + return hv
+ + +
+[docs] + def pareto(self, + f: Tensor) -> list[bool]: + """ + Determines the Pareto dominance of a given set of solutions. + + Args: + f (Tensor): The input series containing the solutions. + + Returns: + list[bool]: A list of boolean values indicating whether each solution is Pareto optimal or not. + """ + + if f.shape[1] <= 1: + raise UnsupportedError('Cannot calculate pareto front for single objective') + if f.ndim != 2: + raise ValueError('Pareto front calculation only supported for 2D tensor') + + return is_non_dominated(f).tolist()
+ + +
+[docs] + def pf_distance(self, + y: pd.Series) -> Tensor: + """ + Calculates the pairwise distance between the given input `y` and the Pareto front. + + Args: + y (pd.Series): The input data. + + Returns: + Tensor: The minimum distance between `y` and the Pareto front. + """ + f = torch.tensor(pd.concat([t.transform_f(y[t.name]) for t in self.target], axis=1).to_numpy()) + pf = self.pareto(torch.tensor(self.f_train.values)) + f_pareto = torch.tensor(self.f_train.values)[pf] + + f = f.unsqueeze(1) # Insert "to_xdim" before "from_xdim" + f_pareto = f_pareto.unsqueeze(0) # Insert "from_xdim" before "to_xdim" + + pairwise_distance = torch.norm(f - f_pareto, p=2, dim=-1) + min_distance = pairwise_distance.min(dim=1).values + + return min_distance
+ + +
+[docs] + @abstractmethod + def fit(self, + Z: pd.DataFrame, + target: Target | list[Target]): + """Fit the optimizer's surrogate models to data""" + pass # pragma: no cover
+ + +
+[docs] + @abstractmethod + def predict(self, + X: pd.DataFrame, + return_f_inv: bool = True, + PI_range: float = 0.7): + """Predict the optimizer's target(s) at the candidate set X""" + pass # pragma: no cover
+ + +
+[docs] + @abstractmethod + def suggest(self): + """Suggest the next optimal experiment(s)""" + pass # pragma: no cover
+ + +
+[docs] + @abstractmethod + def maximize(self): + """Maximize the optimizer's target(s)""" + pass # pragma: no cover
+ + +
+[docs] + @abstractmethod + def save_state(self): + """Save the optimizer to a state dictionary""" + pass # pragma: no cover
+ + +
+[docs] + @classmethod + @abstractmethod + def load_state(cls, + obj_dict: dict): + """Load the optimizer from a state dictionary""" + pass # pragma: no cover
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/optimizer/bayesian.html b/_modules/obsidian/optimizer/bayesian.html new file mode 100644 index 0000000..345a79a --- /dev/null +++ b/_modules/obsidian/optimizer/bayesian.html @@ -0,0 +1,1524 @@ + + + + + + + + + + obsidian.optimizer.bayesian — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.optimizer.bayesian

+"""Bayesian Optimization: Select experiments from the predicted posterior and update the prior"""
+
+from .base import Optimizer
+
+from obsidian.parameters import ParamSpace, Target, Task
+from obsidian.surrogates import SurrogateBoTorch, DNN
+from obsidian.acquisition import aq_class_dict, aq_defaults, aq_hp_defaults, valid_aqs
+from obsidian.surrogates import model_class_dict
+from obsidian.objectives import Index_Objective, Objective_Sequence
+from obsidian.exceptions import IncompatibleObjectiveError, UnsupportedError, UnfitError, DataWarning
+from obsidian.config import TORCH_DTYPE
+
+from botorch.acquisition.objective import MCAcquisitionObjective
+from botorch.optim import optimize_acqf, optimize_acqf_mixed
+from botorch.sampling import SobolQMCNormalSampler
+from botorch.sampling.list_sampler import ListSampler
+from botorch.sampling.index_sampler import IndexSampler
+from botorch.models.model_list_gp_regression import ModelListGP
+from botorch.models.gpytorch import GPyTorchModel
+from botorch.models.model import ModelList
+from botorch.utils.sampling import draw_sobol_samples
+from botorch.utils.multi_objective.box_decompositions.non_dominated import NondominatedPartitioning
+
+import torch
+from torch import Tensor
+import pandas as pd
+import numpy as np
+from typing import Callable
+import warnings
+
+
+
+[docs] +class BayesianOptimizer(Optimizer): + """ + BayesianOptimizer is a class that implements a Bayesian optimization algorithm. + + This class is used to optimize a given function by iteratively selecting the next set of input parameters + based on the results of previous evaluations. It uses a surrogate model to approximate the underlying function + and an acquisition function to determine the next set of parameters to evaluate. + + Args: + X_space (ParamSpace): The parameter space defining the search space for the optimization. + surrogate (str | dict | list[str] | list[dict], optional): The surrogate model(s) to use. + It can be a string representing a single model type, a dictionary specifying multiple model types + with their hyperparameters, or a list of strings or dictionaries. + + Defaults to ``'GP'``. Options are as follows: + + - ``'GP'``: Gaussian Process with default settings (Matern Kernel, Gamma covariance priors) + - ``'MixedGP'``: GP with mixed parameter types (continuous, categorical). Will be re-selected + by default if 'GP' is selected and input space is mixed. + - ``'DKL'``: GP with a NN feature-extractor (deep kernel learning) + - ``'GPflat'``: GP without priors. May result in optimization instability, but removes bias + for special situations. + - ``'GPprior'``: GP with custom priors on the mean, likelihood, and covariance + - ``'MTGP'``: Multi-task GP for multi-output optimization. Will be re-selected by default + if 'GP' is selected and the input space contains Task parameters. + - ``'DNN'``: Dropout neural network. Uses MC sampling to mask neurons during training and + to estimate uncertainty. + + + seed (int | None, optional): The random seed to use. Defaults to ``None``. + verbose (int, optional): The verbosity level. Defaults to ``1``. + + Attributes: + surrogate_type (list[str]): The shorthand name of each surrogate model. + surrogate_hps (list[dict]): The hyperparameters for each surrogate model. + device (str): The device to use for computations ('cuda' if available, 'cpu' otherwise). + is_fit (bool): Indicates whether the surrogate model has been fit to data. + + Raises: + TypeError: If the surrogate argument is not a string, dict, or list of str/dict. + ValueError: If the surrogate dictionary contains more than one surrogate model type. + KeyError: If the surrogate model is not selected from the available models. + ValueError: If the number of responses does not match the number of specified surrogate + + """ +
+[docs] + def __init__(self, + X_space: ParamSpace, + surrogate: str | dict | list[str] | list[dict] = 'GP', + seed: int | None = None, + verbose: int = 1): + + super().__init__(X_space=X_space, seed=seed, verbose=verbose) + + self.surrogate_type = [] # Shorthand name as str (as provided) + self.surrogate_hps = [] # Hyperparameters + + # Surrogate model selection + if not isinstance(surrogate, (str, list, dict)): + raise TypeError('Surrogate argument must be a string, list of strings \ + dict of {surrogate: {hypers}} or list of dicts') + + def _load_surrogate_str(surrogate_str): + # Just a string = use the same model type for each ycol that might appear later + self.surrogate_type.append(surrogate_str) + self.surrogate_hps.append(dict()) + + def _load_surrogate_dict(surrogate_dict): + if len(surrogate_dict) != 1: + raise ValueError('Surrogate dictionary must contain only one surrogate model type') + # Dictionary of dictionaries = hyperparameters may be included + for surrogate_str, surrogate_hps in surrogate_dict.items(): + if not isinstance(surrogate_hps, dict): + raise TypeError('Surrogate dictionary must be a nested dictionary providing hyperparameters') + self.surrogate_type.append(surrogate_str) + self.surrogate_hps.append(surrogate_hps) + + if isinstance(surrogate, str): + _load_surrogate_str(surrogate) + elif isinstance(surrogate, dict): + _load_surrogate_dict(surrogate) + elif isinstance(surrogate, list): + for surrogate_i in surrogate: + if isinstance(surrogate_i, str): + _load_surrogate_str(surrogate_i) + elif isinstance(surrogate_i, dict): + _load_surrogate_dict(surrogate_i) + else: + raise ValueError('Surrogate argument must be a string, dict, or list of str/dict') + + for surrogate_str in self.surrogate_type: + if surrogate_str not in model_class_dict.keys(): + raise KeyError(f'Surrogate model must be selected from one of: {model_class_dict.keys()}') + + self.device = 'cuda' if torch.cuda.is_available() else 'cpu' + + return
+ + + @property + def is_fit(self): + """ + Check if all surrogate mdoels in optimizer are fit + + Returns: + bool: True if the optimizer is fit, False otherwise. + """ + if hasattr(self, 'surrogate'): + return all(model.is_fit for model in self.surrogate) + else: + return False + + def _validate_target(self, + target: Target | list[Target] | None = None): + """ + Validates the target input for the optimization process. + + Args: + target (Target | list[Target] | None, optional): The target object or a list of target objects to be validated. + If None, the target object specified during the initialization of the optimizer will be used. + Defaults to ``None``. + + Raises: + TypeError: If the target is not a Target object or a list of Target objects. + + Returns: + list[Target]: List of validated target(s) + """ + if target is None: + if not hasattr(self, 'target'): + raise TypeError('Target must be a Target object or a list of Target objects') + else: + target = list(self.target) + else: + if not isinstance(target, (Target, list)): + raise TypeError('Target must be a Target object or a list of Target objects') + elif isinstance(target, Target): + target = [target] + elif isinstance(target, list): + if not all(isinstance(t, Target) for t in target): + raise TypeError('Each item in target must be a Target object') + return target + +
+[docs] + def fit(self, + Z: pd.DataFrame, + target: Target | list[Target]): + """ + Fits the BO surrogate model to data. + + Args: + Z (pd.DataFrame): Total dataset including inputs (X) and response values (y) + target (Target or list of Target): The responses (y) to be used for optimization, + packed into a Target object or list thereof + + Returns: + None. Updates the model in self.surrogate + + Raises: + NameError: If the target is not present in the data. + ValueError: If the number of responses does not match the number of specified surrogate models. + """ + + self.target = tuple(self._validate_target(target)) + self.y_names = tuple([t.name for t in self.target]) + self.n_response = len(self.target) + + for t in self.target: + if t.name not in Z.columns: + raise NameError(f"Specified target {t.name} is not present in data") + + # For multi-response, specifying one model type is OK; as this will be used for all responses + if self.n_response > 1: + if len(self.surrogate_type) == 1: + self.surrogate_type *= self.n_response + self.surrogate_hps *= self.n_response + else: + if self.n_response != len(self.surrogate_type): + raise ValueError('Number of responses does not match the number \ + of specified surrogate models') + + # Filter out NaN by X + X_names = list(self.X_space.X_names) + Z_valid = Z.copy().dropna(subset=X_names) + + # Unpack X data + self.X_train = Z_valid[X_names] + self.X_t_train = self.X_space.encode(self.X_train) + # Note: Z is allowed to contain columns that are neither ycols or Xcols; these will get ignored + # Accessing the list(tuple(names)) will enforce that the order of the columns is preserved before fitting + + # Converty y (response) data to f (target) data + self.y_train = pd.concat([Z_valid[t.name] for t in self.target], axis=1) + self.f_train = pd.concat([t.transform_f(Z_valid[t.name], fit=True) for t in self.target], axis=1) + + # Instantiate and fit the model(s) + self.surrogate = [] + for i in range(self.n_response): + self.surrogate.append( + SurrogateBoTorch(model_type=self.surrogate_type[i], seed=self.seed, + verbose=self.verbose, hps=self.surrogate_hps[i])) + + # Handle response NaN values on a response-by-response basis + f_train_i = self.f_train.iloc[:, i] + nan_indices = np.where(f_train_i.isna().values)[0] + X_t_train_valid = self.X_t_train.drop(nan_indices) + f_train_i_valid = f_train_i.drop(nan_indices) + if X_t_train_valid.shape[0] < 1: + raise ValueError(f'No valid data points for response {self.y_names[i]}') + if f_train_i_valid.shape[0] < 1: + raise ValueError(f'No valid response data points for response {self.y_names[i]}') + + # Fit the model for each response + self.surrogate[i].fit(X_t_train_valid, f_train_i_valid, + cat_dims=self.X_space.X_t_cat_idx, task_feature=self.X_space.X_t_task_idx) + + if self.verbose > 0: + print(f'{self.surrogate_type[i]} model has been fit \ + to data with a train-score of: {self.surrogate[i].score:.3g} for response: {self.y_names[i]}') + return
+ + +
+[docs] + def save_state(self) -> dict: + """ + Saves the parameters of the Bayesian Optimizer so that they can be reloaded without fitting. + + Returns: + dict: A dictionary containing the fit parameters for later loading. + + Raises: + UnfitError: If the surrogate model has not been fit before saving the optimizer. + """ + + if not self.is_fit: + raise UnfitError('Surrogate model must be fit before saving optimizer') + + # Prepare a dictionary to describe the state + config_save = {'opt_attrs': {}, + 'X_space': self.X_space.save_state(), + 'surrogate_spec': [{func: hps} for func, hps in zip(self.surrogate_type, self.surrogate_hps)], + 'target': [t.save_state() for t in self.target]} + + # Select some optimizer attributes to save directly + opt_attrs = ['X_train', 'y_train', + 'y_names', 'n_response', + 'seed'] + + for attr in opt_attrs: + if isinstance(getattr(self, attr), (pd.Series, pd.DataFrame)): + config_save['opt_attrs'][attr] = getattr(self, attr).to_dict() + else: + config_save['opt_attrs'][attr] = getattr(self, attr) + + # Unpack the fit parameters of each surrogate model, if present + if self.surrogate: + model_states = [] + # Save each surrogate model using surrogate.save() methods + for surrogate in self.surrogate: + model_states.append(surrogate.save_state()) + + config_save['model_states'] = model_states + + return config_save
+ + + def __repr__(self): + return f'BayesianOptimizer(X_space={self.X_space}, surrogate={self.surrogate_type}, target={getattr(self, "target", None)})' + +
+[docs] + @classmethod + def load_state(cls, + config_save: dict): + """ + Loads the parameters of the Bayesian Optimizer from a previously fit optimizer. + + Args: + config_save (dict): A dictionary containing the fit parameters for later loading. + + Returns: + None. Updates the parameters of the BayesianOptimizer and its surrogate model. + + Raises: + ValueError: If the number of saved models does not match the number of named models. + """ + + new_opt = cls(X_space=ParamSpace.load_state(config_save['X_space']), + surrogate=config_save['surrogate_spec']) + new_opt.target = [Target.load_state(t) for t in config_save['target']] + + # Directly unpack all of the entries in opt_attrs + for k, v in config_save['opt_attrs'].items(): + setattr(new_opt, k, v) + + # Unpack and encode/transform the data objects if present + data_objects = ['X_train', 'y_train'] + if all(hasattr(new_opt, attr) for attr in data_objects): + new_opt.X_train = pd.DataFrame(new_opt.X_train) + new_opt.X_t_train = new_opt.X_space.encode(new_opt.X_train) + new_opt.y_train = pd.DataFrame(new_opt.y_train, columns=new_opt.y_names) + + f_train = pd.DataFrame() + for t, y in zip(new_opt.target, new_opt.y_train.columns): + f = t.transform_f(new_opt.y_train[y], fit=True) + f_train = pd.concat([f_train, f.to_frame()], axis=1) + new_opt.f_train = f_train + + # Unpack the models and parameteres if present + if 'model_states' in config_save: + if len(new_opt.surrogate_type) != len(config_save['model_states']): + raise ValueError('The number of saved models does not match the number of named models') + + # Reload each surrogate model using surrogate.load() methods + new_opt.surrogate = [] + for obj_dict in config_save['model_states']: + new_opt.surrogate.append(SurrogateBoTorch.load_state(obj_dict)) + + return new_opt
+ + +
+[docs] + def predict(self, + X: pd.DataFrame, + return_f_inv: bool = True, + PI_range: float = 0.7) -> pd.DataFrame: + """ + Predicts a response over a range of experiments using the surrogate function. + + Args: + X (pd.DataFrame): Experiments to predict over. + return_f_inv (bool, optional): Whether or not to return the inverse-transformed objective function, + which is the raw response (unscored). The default is ``True``. Most internal calls set to ``False`` to handle + the transformed objective function. + PI_range (float, optional): The nominal coverage range for the returned prediction interval + + Returns: + pd.DataFrame: Mean prediction and prediction interval for each response + + Raises: + TypeError: If the input is not a DataFrame. + UnfitError: If the surrogate model has not been fit before predicting. + ValueError: If the prediction interval range is greater than 1. + NameError: If the input does not contain all of the required predictors from the training set. + """ + + if not isinstance(X, pd.DataFrame): + raise TypeError('X must be pd.DataFrame') + if not self.is_fit: + raise UnfitError('Surrogate model must be fit before predicting') + if PI_range >= 1: + raise ValueError('Prediction interval range must be < 1 \ + (100% coverage of prob. density func.)') + if not all(col in X.columns for col in self.X_train.columns): + raise NameError('X for prediction does not contain all of the \ + required predictors from the training set') + + X_names = list(self.X_space.X_names) + X_pred = X[X_names].dropna(subset=X_names) # Reinforce order and non-nan before proceeding + nan_indices = np.where(pd.isnull(X[X_names]).any(axis=1))[0].tolist() + if nan_indices: + warnings.warn(f'NaN values in X_pred filtered out at indices: {nan_indices}', DataWarning) + + # Scale and encode X + X_t = self.X_space.encode(X_pred) + + preds = pd.DataFrame() + for i in range(self.n_response): + mu, sd = self.surrogate[i].predict(X_t) # Returns pd.DataFrame/Series objects + target_i = self.target[i] + _, lb = self.surrogate[i].predict(X_t, q=(1-PI_range)/2) + _, ub = self.surrogate[i].predict(X_t, q=1-(1-PI_range)/2) + name = self.y_names[i] + if return_f_inv: + mu = target_i.transform_f(mu, inverse=True).rename(name+' (pred)') + lb = target_i.transform_f(lb, inverse=True).rename(name+' lb') + ub = target_i.transform_f(ub, inverse=True).rename(name+' ub') + else: + mu = pd.Series(mu, name=name+'_t (pred)') + lb = pd.Series(lb, name=name+'_t lb') + ub = pd.Series(ub, name=name+'_t ub') + predict_i = pd.concat([mu, lb, ub], axis=1) + preds = pd.concat([preds, predict_i], axis=1) + + return preds
+ + + def _validate_hypers(self, + o_dim: int, + acquisition: str | dict,) -> tuple[dict, dict]: + """ + Validates the acquisition functions and their hyperparameters. + + Args: + o_dim (int): The output dimensionality of the final objective function + acquisition (str | dict): Acquisition function name (str) or dictionary + containing the acquisition function name and its hyperparameters. + + Returns: + tuple[dict, dict]. Validated acquisition functions and hyperparmeters + + Raises: + ValueError: If the number of hyperparameters does not match the number of acquisition functions. + TypeError: If the hyperparameters are not provided as a dictionary. + ValueError: If an unknown hyperparameter is passed for an acquisition function + UnsupportedError: If the acquisition function is not supported for the optimization type. + ValueError: If any hyperparameters are required but not provided. + + """ + # If the item is a string, use default hypers starting with an empty dict + aq_hps = {} + if isinstance(acquisition, str): + aq_str = acquisition + hps = {} + # If the item is a dict, the structure is {name: {hypers}} + else: + # Validate the hypers if provided + if len(acquisition.items()) != 1: + raise ValueError('One dictionary of hyperparameters \ + must be provided for each acquisition function') + aq_str, hps = list(acquisition.items())[0] + if not isinstance(hps, dict): + raise TypeError('Hyperparameters must be provided as a dictionary') + if not all(key in aq_hp_defaults[aq_str].keys() for key in hps.keys()): + raise ValueError('Unknown hyperpameter amongst {hps.keys()} \ + selected for {aq_str}, select from one of \ + {aq_hp_defaults[aq_str].keys()}') + aq_hps.update(hps) + + optim_type = 'single' if o_dim == 1 else 'multi' + + # Validate the acquisition name + if aq_str not in valid_aqs[optim_type]: + raise UnsupportedError(f'Each acquisition function must be \ + selected from: {valid_aqs[optim_type]}') + + # Fill in empty hypers with defaults, as appropriate + for key, defaults in aq_hp_defaults[aq_str].items(): + if hps.get(key) is None: + if not defaults['optional']: + raise ValueError(f'Must specify hyperpameter value {key} for {aq_str}') + if key in ['scalarization_weights', 'weights']: + aq_hps[key] = defaults['val'] * o_dim + else: + aq_hps[key] = defaults['val'] + + return (aq_str, aq_hps) + + def _parse_aq_kwargs(self, + aq: str, + hps: dict, + m_batch: int, + target_locs: list[int], + X_t_pending: Tensor | None = None, + objective: MCAcquisitionObjective | None = None) -> dict: + """ + Parses the acquisition function keyword arguments based on the selected acquisition + function and hyperparameters. + + Args: + aq (str): The name of the acquisition function. + hps (dict): The hyperparameters for the acquisition function. + target_locs (list[int]): Indices of trained targets to use for objective function + X_t_pending (Tensor, optional): Suggested points yet to be run + objective (GenericMCMultiOutputObjective or GenericMCObjective, optional): + The objective used foroptimization after calling the target models. + Default is ``None``. + + Returns: + dict: The parsed acquisition function keyword arguments. + """ + + aq_kwargs = {} + + # Establish baseline X from training and pending + X_train = torch.tensor(self.X_space.encode(self.X_train).values, dtype=TORCH_DTYPE) + if X_t_pending is not None: + X_baseline = torch.concat([X_train, X_t_pending], axis=0) + else: + X_baseline = X_train + + # Calculate the performance on baseline X + f_all = [] + for i in target_locs: + X_b = pd.DataFrame(X_baseline.numpy(), + columns=[col for col in self.X_t_train.columns + if col not in self.X_space.X_task]) + f_i, _ = self.surrogate[i].predict(X_b) + f_all.append(f_i) + f_t = torch.stack(f_all, axis=1) + + # If using an objective, want to calculate EI/PI from here + o = f_t if not objective else objective(f_t.unsqueeze(0), X_baseline).squeeze(0) + + # Improvement aqs based on inflation or deflation of best point + if aq in ['EI', 'PI']: + o_max = o.max(dim=0).values * (1+hps['inflate']) + aq_kwargs.update({'best_f': o_max}) + + # UCB based on: mu + sqrt(beta) * sqrt(variance) = mu + sqrt(beta) * sd + if aq == 'UCB': + aq_kwargs['beta'] = hps['beta'] + + # Noisy aqs require X_train reference + if aq in ['NEI', 'NEHVI', 'NParEGO']: + aq_kwargs['X_baseline'] = X_baseline + + # Hypervolume requires reference point + if aq in ['EHVI', 'NEHVI']: + + # The reference point must be in the objective space, by default + # use the minimum point - 10% of the range + ref_point = hps['ref_point'] + if ref_point is None: + max = o.max(dim=0).values + min = o.min(dim=0).values + ref_point = min - 0.1 * (max - min) + else: + ref_point = torch.tensor(ref_point) + + aq_kwargs['ref_point'] = ref_point + + if aq in ['NParEGO', 'NEHVI']: + aq_kwargs['prune_baseline'] = True + + if aq == 'EHVI': + aq_kwargs['partitioning'] = NondominatedPartitioning(aq_kwargs['ref_point'], Y=o) + + if aq == 'NIPV': + X_bounds = torch.tensor([[0.0, 1.0]]*self.X_space.n_tdim, dtype=TORCH_DTYPE).T.to(self.device) + qmc_samples = draw_sobol_samples(bounds=X_bounds, n=128, q=m_batch) + aq_kwargs['mc_points'] = qmc_samples.squeeze(-2) + + return aq_kwargs + +
+[docs] + def suggest(self, + m_batch: int = 1, + target: Target | list[Target] | None = None, + acquisition: list[str] | list[dict] = None, + optim_sequential: bool = True, + optim_samples: int = 512, + optim_restarts: int = 10, + objective: MCAcquisitionObjective | None = None, + out_constraints: list[Callable] | None = None, + eq_constraints: tuple[Tensor, Tensor, float] | None = None, + ineq_constraints: tuple[Tensor, Tensor, float] | None = None, + nleq_constraints: tuple[Callable, bool] | None = None, + task_index: int = 0, + fixed_var: dict[str: float | str] | None = None, + X_pending: pd.DataFrame | None = None, + eval_pending: pd.DataFrame | None = None) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: + """ + Suggest future experiments based on a maximization of some acquisition + function calculated from the expectation of a surrogate model. + + Args: + m_batch (int, optional): The number of experiments to suggest at once. The default is ``1``. + target (Target or list of Target, optional): The response(s) to be used for optimization, + acquisition (list of str or list of dict, optional): Indicator for the desired acquisition function(s). + A list will propose experiments for each acquisition function based on ``optim_sequential``. + + The default is ``['NEI']`` for single-output and ``['NEHVI']`` for multi-output. + Options are as follows: + + - ``'EI'``: Expected Improvement (relative to best of ``y_train``). Accepts hyperparameter + ``'inflate'``, a positive or negative float to inflate/deflate the best point for explore/exploit. + - ``'NEI'``: Noisy Expected Improvement. More robust than ``EI`` and uses all of ``y_train``, + but accepts no hyperparameters. + - ``'PI'``: Probability of Improvement (relative to best of ``y_train``). Accepts hyperparameter + ``'inflate'``, a positive or negative float to inflate/deflate the best point for explore/exploit. + - ``'UCB'``: Upper Confidence Bound. Accepts hyperparameter ``'beta'``, a positive float which sets + the number of standard deviations above the mean. + - ``'SR'``: Simple Regret + - ``'RS'``: Random Sampling + - ``'Mean'``: Mean of the posterior distribution (pure exploitation/maximization of objective) + - ``'SF'``: Space Filling. Requests points that maximize the minimumd distance to ``X_train`` based + on Euclidean distance. + - ``'NIPV'``: Negative Integrated Posterior Variance. Requests the point which most improves the prediction + interval for a random selection of points in the design space. Used for active learning. + - ``'EHVI'``: Expected Hypervolume Improvement. Can accept a ``ref_point``, otherwise a point just + below the minimum of ``y_train``. + - ``'NEHVI'``: Noisy Expected Hypervolume Improvement. Can accept a ``ref_point``, otherwise a point + just below the minimum of ``y_train``. + - ``'NParEGO'``: Noisy Pareto Efficient Global Optimization. Can accept ``scalarization_weights``, a + list of weights for each objective. + + optim_sequential (bool, optional): Whether or not to optimize batch designs sequentially + (by fantasy) or simultaneously. Default is ``True``. + optim_samples (int, optional): The number of samples to use for quasi Monte Carlo sampling + of the acquisition function. Also used for initializing the acquisition optimizer. + The default value is ``512``. + optim_restarts (int, optional): The number of restarts to use in the global optimization + of the acquisition function. The default value is ``10``. + objective (MCAcquisitionObjective, optional): The objective function to be used for optimization. + The default is ``None``. + out_constraints (list of Callable, optional): A list of constraints to be applied to the output space. + The default is ``None``. + eq_constraints (tuple of Tensor, Tensor, float, optional): A tuple of tensors representing the equality + constraints, the target values, and the tolerance. The default is ``None``. + ineq_constraints (tuple of Tensor, Tensor, float, optional): A tuple of tensors representing the inequality + constraints, the target values, and the tolerance. The default is ``None``. + nleq_constraints (tuple of Callable, bool, optional): A tuple of functions representing the nonlinear + inequality constraints and a boolean indicating whether the constraints are active. + The default is ``None``. + task_index (int, optional): The index of the task to optimize for multi-task models. The default is ``0``. + fixed_var (dict(str:float), optional): Name of a variable and setting, over which the + suggestion should be fixed. Default values is ``None`` + X_pending (pd.DataFrame, optional): Experiments that are expected to be run before the next optimal set + eval_pending (pd.DataFrame, optional): Acquisition values associated with X_pending + + Returns: + tuple[pd.DataFrame, pd.DataFrame] = (X_suggest, eval_suggest) + X_suggest (pd.DataFrame): Experiment matrix of real input variables, + selected by optimizer. + eval_suggest (pd.DataFrame): Mean results (response, prediction interval, f(response), obj + function for + each suggested experiment. + + Raises: + UnfitError: If the surrogate model has not been fit before suggesting new experiments. + TypeError: If the target is not a Target object or a list of Target objects. + IncorrectObjectiveError: If the objective does not successfully execute on a sample. + TypeError: If the acquisition is not a list of strings or dictionaries. + UnsupportedError: If the provided acquisition function does not support output constraints. + UnsupportedError: If nonlinear constraints are provided with discrete features. + + """ + + if not self.is_fit: + raise UnfitError('Surrogate model must be fit before suggesting new experiments') + + # Use indexing to handle if suggestions are made for a subset of fit targets/surrogates + target = self._validate_target(target) + target_locs = [self.y_names.index(t.name) for t in target] + model_list = [one_surrogate.torch_model for i, one_surrogate in enumerate(self.surrogate) if i in target_locs] + if all(isinstance(m, GPyTorchModel) for m in model_list): + model = ModelListGP(*model_list) + else: + model = ModelList(*model_list) + + # Make sure that model/objective outputs match the input requirements for aqs + # In order to determine the number of outputs, considering objectives, + # just call the objective on a random sample, and check the output dims + if objective: + try: + X_sample = self.X_train.iloc[0, :].to_frame().T + eval_suggest = self.evaluate(X_sample, target=target, objective=objective) + o_dim = len([col for col in eval_suggest.columns if 'Objective' in col]) + except Exception: + raise IncompatibleObjectiveError('Objective(s) did not successfully execute on sample') + else: + o_dim = len(target_locs) + + optim_type = 'single' if o_dim == 1 else 'multi' + + # Default to noisy expected improvement if no aq method is provided + if not acquisition: + acquisition = [aq_defaults[optim_type]] + + if not isinstance(acquisition, list): + raise TypeError('acquisition must be a list of strings or dictionaries') + if not all(isinstance(item, (str, dict)) for item in acquisition): + raise TypeError('Each item in acquisition list must be either a string or a dictionary') + + # Compute static variable inputs + fixed_features_list = self._fixed_features(fixed_var) + + # Set up the sampler, for MC-based optimization of acquisition functions + if not isinstance(model, ModelListGP): + samplers = [] + for m in model.models: + if isinstance(m, DNN): + sampler_i = IndexSampler(sample_shape=torch.Size([optim_samples]), seed=self.seed).to(self.device) + else: + sampler_i = SobolQMCNormalSampler(sample_shape=torch.Size([optim_samples]), seed=self.seed).to(self.device) + samplers.append(sampler_i) + sampler = ListSampler(*samplers) + else: + sampler = SobolQMCNormalSampler(sample_shape=torch.Size([optim_samples]), seed=self.seed).to(self.device) + X_bounds = torch.tensor([[0.0, 1.0]]*self.X_space.n_tdim, dtype=TORCH_DTYPE).T.to(self.device) + + # Set up master lists to hold the candidates from multi-acquisition results + candidates_all = [] + eval_suggest = pd.DataFrame() + + # Incorporate previously suggested X values, if provided + if X_pending is not None: + m_pending = X_pending.shape[0] + candidates_pending = torch.tensor(self.X_space.encode(X_pending).values) + candidates_all.append(candidates_pending) + X_t_pending = torch.concat(candidates_all) + if eval_pending is None: + eval_suggest['aq Method'] = ['User Provided']*m_pending + else: + eval_suggest = eval_pending + else: + X_t_pending = None + + # Select the task to optimize for a multi-task model + if self.X_space.X_task: + if objective is not None: + objective = Objective_Sequence([Index_Objective(task_index), objective]) + task_param = next(x for x in self.X_space if isinstance(x, Task)) + task_name = task_param.name + task_value = task_param.encode([task_param.categories[task_index]]) + + # Proceed with the optimization over each set of acquisition/hypers + for aq_i in acquisition: + # Extract acq function names and custom hyperparameters from the 'acquisition' list in config + aq_str, aq_hps = self._validate_hypers(o_dim, aq_i) + + # Use aq_kwargs so that extra unnecessary ones in hps get removed for certain aq funcs + aq_kwargs = {'model': model, 'sampler': sampler, 'X_pending': X_t_pending} + + if aq_str != 'NIPV': + aq_kwargs['objective'] = objective + else: + aq_kwargs['sampler'] = None + + # Type check for constraints + for constraint_type in eq_constraints, ineq_constraints, nleq_constraints, out_constraints: + if constraint_type: + if not isinstance(constraint_type, list): + raise TypeError('Constraints must be passed as lists of callables') + + if aq_str in ['UCB', 'Mean', 'TS', 'SF', 'SR', 'NIPV']: + if out_constraints is not None: + raise UnsupportedError('Provided aquisition function does not support output constraints') + else: + aq_kwargs['constraints'] = out_constraints + + # Input constraints are used by optim_acqf and friends + optim_kwargs = {'equality_constraints': eq_constraints, + 'inequality_constraints': ineq_constraints, + 'nonlinear_inequality_constraints': nleq_constraints} + + optim_options = {} # Can optionally specify batch_limit or max_iter + + # If nonlinear constraints are used, BoTorch doesn't provide an ic_generator + # Must provide manual samples = just use random initialization + if nleq_constraints is not None: + X_ic = torch.ones((optim_samples, 1 if fixed_features_list else m_batch, self.X_space.n_tdim))*torch.rand(1) + optim_kwargs['batch_initial_conditions'] = X_ic + if fixed_features_list: + raise UnsupportedError('Nonlinear constraints are not supported with discrete features.') + + aq_kwargs.update(self._parse_aq_kwargs(aq_str, aq_hps, m_batch, target_locs, X_t_pending, objective)) + + # Hypervolume aqs fail with X_t_pending when optim_sequential=True + if aq_i in ['NEHVI', 'EHVI']: + optim_sequential = False + + # If it's random search, no need to do optimization; Otherwise, initialize the aq function and optimize + if aq_str == 'RS': + candidates = torch.rand((m_batch, self.X_space.n_tdim), dtype=TORCH_DTYPE) + else: + aq_func = aq_class_dict[aq_str](**aq_kwargs).to(TORCH_DTYPE) + + # If there are any discrete values, we must used the mixed integer optimization + if fixed_features_list: + candidates, _ = optimize_acqf_mixed(acq_function=aq_func, bounds=X_bounds, + fixed_features_list=fixed_features_list, + q=m_batch, # Always sequential + num_restarts=optim_restarts, raw_samples=optim_samples, + options=optim_options, + **optim_kwargs) + else: + candidates, _ = optimize_acqf(acq_function=aq_func, bounds=X_bounds, + q=m_batch, + sequential=optim_sequential, + num_restarts=optim_restarts, raw_samples=optim_samples, + options=optim_options, + **optim_kwargs) + + candidates_i = self.X_space.decode( + pd.DataFrame(candidates.detach().cpu().numpy(), + columns=[col for col in self.X_t_train.columns if col not in self.X_space.X_task])) + + if X_t_pending is not None: + X_t_pending_i = X_t_pending + else: + X_t_pending_i = None + + eval_i = self.evaluate(candidates_i, X_t_pending_i, + target=target, acquisition=aq_i, objective=objective, eval_aq=True) + eval_suggest = pd.concat([eval_suggest, eval_i], axis=0).reset_index(drop=True) + + candidates_all.append(candidates) + X_t_pending = torch.concat(candidates_all) + + candidates_all = pd.DataFrame(X_t_pending.detach().cpu().numpy(), + columns=[col for col in self.X_t_train.columns + if col not in self.X_space.X_task]) + + if self.X_space.X_task: + candidates_all[task_name] = task_value + + # Arrange suggested experiments into a single dataframe, and compute predictions + X_suggest = self.X_space.decode(candidates_all) + + return X_suggest, eval_suggest
+ + +
+[docs] + def evaluate(self, + X_suggest: pd.DataFrame, + X_t_pending: Tensor | None = None, + target: Target | list[Target] | None = None, + acquisition: str | dict = None, + objective: MCAcquisitionObjective | None = None, + eval_aq: bool = False) -> pd.DataFrame: + """ + Args: + X_suggest (pd.DataFrame): Experiment matrix of real input variables, selected by optimizer. + X_t_pending (Tensor): Suggested experiments yet to be run + target (Target or list of Target, optional): The response(s) to be used for optimization, + acquisition (str | dict, optional): Acquisition function name (str) or dictionary + containing the acquisition function name and its hyperparameters. + objective (MCAcquisitionObjective, optional): The objective function to be used for optimization. + The default is ``None``. + eval_aq (bool, optional): Whether or not to also evaluate the aq function. The default is ``False``. + + Returns: + pd.DataFrame: Response prediction, pred interval, transformed mean, aq value, + and objective function evaluation(s) + + """ + + if not self.is_fit: + raise UnfitError('Surrogate model must be fit before suggesting new experiments') + + # Use indexing to handle if suggestions are made for a subset of fit targets/surrogates + target = self._validate_target(target) + target_locs = [self.y_names.index(t.name) for t in target] + + # Begin evaluation with y_predict with pred interval + eval_suggest = self.predict(X_suggest) + X_t = torch.tensor(self.X_space.encode(X_suggest).values, dtype=TORCH_DTYPE).to(self.device) + X_t_train = torch.tensor(self.X_space.encode(self.X_train).values, dtype=TORCH_DTYPE).to(self.device) + + # Evaluate f_predict on new and pending points + f_all = [] + f_train_all = [] + f_pending_all = [] + for loc in target_locs: + # Predict: new suggestions + t_model = self.surrogate[loc] + mu_i, _ = t_model.predict(self.X_space.encode(X_suggest)) + f_all.append(mu_i.unsqueeze(1)) + eval_suggest[f'f({self.target[loc].name})'] = mu_i + + # Training data on select targets + mu_i_train, _ = t_model.predict(self.X_space.encode(self.X_train)) + f_train_all.append(mu_i_train.unsqueeze(1)) + + # Pending points if provided + if X_t_pending is not None: + mu_i_pending, _ = t_model.predict(pd.DataFrame(X_t_pending, + columns=[col for col in self.X_t_train.columns + if col not in self.X_space.X_task])) + f_pending_all.append(mu_i_pending.unsqueeze(1)) + + f_t = torch.concat(f_all, dim=1) + f_t_train = torch.concat(f_train_all, dim=1) + if X_t_pending is not None: + f_t_pending = torch.concat(f_pending_all, dim=1) + + if objective: + # Convert f_predict to tensor and evaluate objective + # Must add sample dimension to f_t + o = objective(f_t.unsqueeze(0), X_t).squeeze(0) + if o.ndim < 2: + o = o.unsqueeze(1) # Rearrange into m x o + + # Store multiple objectives if applicable + for o_i in range(o.shape[-1]): + eval_suggest[f'Objective {o_i+1}'] = o[:, o_i].detach().cpu().numpy() + + # Also calculate for training and pending data (for total hv and pareto calcs) + o_train = objective(f_t_train.unsqueeze(0), X_t_train).squeeze(0) + if o_train.ndim < 2: + o_train = o_train.unsqueeze(1) + if X_t_pending is not None: + o_pending = objective(f_t_pending.unsqueeze(0), X_t_pending).squeeze(0) + if o_pending.ndim < 2: + o_pending = o_pending.unsqueeze(1) + + # Calculate output dimensionality and evaluate acquisition + o_dim = o.shape[-1] + + else: + o_dim = len(target_locs) + + optim_type = 'single' if o_dim == 1 else 'multi' + + if eval_aq: + # Default to noisy expected improvement if no aq method is provided + if not acquisition: + acquisition = [aq_defaults[optim_type]] + + if not isinstance(acquisition, (str, dict)): + raise TypeError('Acquisition must be either a string or a dictionary') + + # Extract acq function names and custom hyperparameters from the 'acquisition' list in config + aq_str, aq_hps = self._validate_hypers(o_dim, acquisition) + + model_list = [one_surrogate.torch_model for i, one_surrogate in enumerate(self.surrogate) if i in target_locs] + if all(isinstance(m, GPyTorchModel) for m in model_list): + model = ModelListGP(*model_list) + else: + model = ModelList(*model_list) + + # Use aq_kwargs so that extra unnecessary ones in hps get removed for certain aq funcs + aq_kwargs = {'model': model, 'X_pending': X_t_pending} + if aq_str != 'NIPV': + aq_kwargs['objective'] = objective + + aq_kwargs.update(self._parse_aq_kwargs(aq_str, aq_hps, X_suggest.shape[0], target_locs, X_t_pending, objective)) + + # If it's random search, no need to evaluate aq + if aq_str == 'RS': + a_joint = torch.tensor([float('nan')]).repeat(X_t.shape[0]).unsqueeze(1) + else: + aq_func = aq_class_dict[aq_str](**aq_kwargs) + + # Evaluate acquisition on individual samples, then jointly + a = [] + for x_i in X_t: + a_i = aq_func(x_i.unsqueeze(0)) + a.append(a_i.detach().cpu()) + a = torch.concat(a).unsqueeze(1) + a_joint = aq_func(X_t).repeat(X_t.shape[0]).unsqueeze(1) # Rearrange into m x 1 + + eval_suggest['aq Value'] = a.numpy() + + eval_suggest['aq Value (joint)'] = a_joint.detach().cpu().numpy() + eval_suggest['aq Method'] = [aq_str]*X_t.shape[0] + + # For multi-output evaluations, calculate pareto and hv considering objectives + if o_dim > 1: + if objective is None: + o = f_t + o_list = [f_t, f_t_train] + if X_t_pending is not None: + o_list.append(f_t_pending) + else: + o_list = [o, o_train] + if X_t_pending is not None: + o_list.append(o_pending) + o_all = torch.concat(o_list, dim=0) + + hv = self.hypervolume(o_all) + eval_suggest['Expected Hypervolume (joint)'] = hv + pf = self.pareto(o_all) + eval_suggest['Expected Pareto'] = pf[-o.shape[0]:] + + return eval_suggest
+ + +
+[docs] + def maximize(self, + optim_samples=1026, + optim_restarts=50, + fixed_var: dict[str: float | str] | None = None) -> tuple[pd.DataFrame, pd.DataFrame]: + """ + Predicts the conditions which return the maximum response value within the parameter space. + + Args: + optim_samples (int): The number of samples to be used for optimization. Default is ``1026``. + optim_restarts (int): The number of restarts for the optimization process. Default is ``50``. + fixed_var (dict(str:float), optional): Name of a variable and setting, over which the + suggestion should be fixed. Default values is ``None`` + Returns: + tuple[pd.DataFrame, pd.DataFrame] = (X_suggest, eval_suggest) + X_suggest (pd.DataFrame): Experiment matrix of real input variables, + selected by optimizer. + y_suggest (pd.DataFrame): Mean results and prediction interval for + each suggested experiment. + """ + + X_suggest = pd.DataFrame() + eval_suggest = pd.DataFrame() + + for target in self.target: + X_suggest_i, eval_suggest_i = self.suggest( + m_batch=1, acquisition=['Mean'], optim_samples=optim_samples, optim_restarts=optim_restarts, target=target) + X_suggest = pd.concat([X_suggest, X_suggest_i], axis=0) + eval_suggest = pd.concat([eval_suggest, eval_suggest_i], axis=0) + + return X_suggest, eval_suggest
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/parameters/base.html b/_modules/obsidian/parameters/base.html new file mode 100644 index 0000000..d44d7a5 --- /dev/null +++ b/_modules/obsidian/parameters/base.html @@ -0,0 +1,565 @@ + + + + + + + + + + obsidian.parameters.base — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.parameters.base

+"""Parameter class definition"""
+
+from abc import ABC, abstractmethod
+
+
+
+[docs] +class Parameter(ABC): + """ + Base class for obsidian parameters. + """ + +
+[docs] + def __init__(self, + name: str): + self.name = name
+ + + def __repr__(self): + return f"{self.__class__.__name__}(name={self.name})" + + @abstractmethod + def _validate_value(self, + value: int | float | str): + """Validate data inputs""" + pass # pragma: no cover + +
+[docs] + @abstractmethod + def encode(X): + """Encode parameter to a format that can be used for training""" + pass # pragma: no cover
+ + +
+[docs] + @abstractmethod + def decode(X): + """Decode parameter from transformed space""" + pass # pragma: no cover
+ + +
+[docs] + def save_state(self) -> dict: + """ + Save the state of the Parameter object. + + Returns: + dict: A dictionary containing the state of the object. + """ + obj_dict = vars(self) + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, + obj_dict: dict): + """ + Load the state of the Parameter object from a dictionary. + + Args: + obj_dict (dict): A dictionary containing the state of the object. + + Returns: + Parameter: A new instance of the Parameter class with the loaded state. + """ + return cls(**obj_dict)
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/parameters/continuous.html b/_modules/obsidian/parameters/continuous.html new file mode 100644 index 0000000..2bec646 --- /dev/null +++ b/_modules/obsidian/parameters/continuous.html @@ -0,0 +1,597 @@ + + + + + + + + + + obsidian.parameters.continuous — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.parameters.continuous

+"""Parameters that can be sampled continuously between a minimum and maximum"""
+
+from .base import Parameter
+from .utils import transform_with_type
+
+import warnings
+import numpy as np
+
+
+
+[docs] +class Param_Continuous(Parameter): + """ + Represents a continuous parameter. + + Attributes: + name (str): The name of the parameter. + min (int or float): The minimum value of the parameter. + max (int or float): The maximum value of the parameter. + + Properties: + range (int): The range of the parameter (max - min). + + """ + + @transform_with_type + def unit_map(self, X: np.ndarray): + """Map from measured to 0,1 space""" + return (X-self.min)/self.range if self.range != 0 else 0*X + + @transform_with_type + def unit_demap(self, X: np.ndarray): + """Map from 0,1 to measured space""" + return X*self.range+self.min + + encode = unit_map + + decode = unit_demap + + @property + def range(self): + """The range of the parameter (max - min)""" + return self.max-self.min + + def __repr__(self): + """String representation of object""" + return f"{self.__class__.__name__}(name={self.name}, min={self.min}, max={self.max})" + + def _validate_value(self, value: int | float): + """ + Validates if the given value is within the specified range. + + Args: + value (int | float): The value to be validated. + + Raises: + TypeError: If the value is not a number. + ValueError: If the value is outside of the specified range. + """ + if not isinstance(value, (int, float)): + raise TypeError(f'Value {value} is not a number') + if (value < self.min) or (value > self.max): + raise ValueError(f'Value {value} is outside of range {self.min} to {self.max}') + +
+[docs] + def __init__(self, + name: str, + min: int | float, + max: int | float): + super().__init__(name=name) + if max < min: + warnings.warn(f'Minimum value {min} is greater than maximum value {max}. Auto-swapping values.', UserWarning) + min, max = max, min + self.min = min + self.max = max + for val in [min, max]: + self._validate_value(val)
+
+ + + +
+[docs] +class Param_Observational(Param_Continuous): + """ + This is an observational numeric variable that is used for fitting but is not leveraged during optimization + + Attributes: + name (str): The name of the parameter. + min (int or float): The minimum value of the parameter. + max (int or float): The maximum value of the parameter. + design_point (int or float): The point which the observational value will be locked to during experiment optimization + + """ +
+[docs] + def __init__(self, + name: str, + min: int | float, + max: int | float, + design_point: int | float | None = None): + super().__init__(name=name, min=min, max=max) + self.design_point = design_point = design_point if design_point is not None else max
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/parameters/discrete.html b/_modules/obsidian/parameters/discrete.html new file mode 100644 index 0000000..4c50eba --- /dev/null +++ b/_modules/obsidian/parameters/discrete.html @@ -0,0 +1,754 @@ + + + + + + + + + + obsidian.parameters.discrete — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.parameters.discrete

+"""Parameters that can only be sampled at specific values"""
+
+from .base import Parameter
+from .utils import transform_with_type
+
+from obsidian.config import CAT_SEP
+
+from numpy.typing import ArrayLike
+
+import pandas as pd
+import numpy as np
+
+
+
+[docs] +class Param_Discrete(Parameter): + """ + Represents a discrete parameter. + + Attributes: + name (str): The name of the parameter. + categories (list[str]): The categories of the parameter. + + Properties: + min (int): The minimum value of the parameter (always 0). + nc (int): The number of categories. + max (int): The maximum value of the parameter (nc - 1). + + """ + + @transform_with_type + def unit_map(self, X: np.ndarray): + """ + Maps the input values to the unit interval [0, 1] based on the categories. + + Parameters: + X (np.ndarray): The input values to be mapped. + + Returns: + np.ndarray: Values mapped to the unit interval [0, 1]. + """ + X_str = X.flatten().astype('U11') + indices = [self.categories.index(x) for x in X_str] + return (np.array(indices)/self.nc).reshape(X.shape) + + @transform_with_type + def unit_demap(self, X: np.ndarray): + """ + Maps continuous values to discrete categories. + + Parameters: + X (np.ndarray): The input values in the interval [0,1] + + Returns: + np.ndarray: Values mapped back to the real space. + """ + + return np.array(self.categories)[(X.flatten()*self.nc) + .astype('int')].reshape(X.shape).astype('U11') + + @property + def min(self): + """Minimum parameter value (always 0 for discrete)""" + return 0 + + @property + def nc(self): + """Number of discrete categories""" + return len(self.categories) + + @property + def max(self): + """Maximum parameter value (nc-1)""" + return self.nc-1 + + def _validate_value(self, + value: str): + """ + Validates if the given value is in the list of categories. + + Args: + value (str): The value to be validated. + + Raises: + KeyError: If the value is not in the list of categories. + TypeError: If the value is not a string + """ + if not isinstance(value, str): + raise TypeError(f'Value {value} is not a string') + + if value not in self.categories: + raise ValueError(f'Value {value} is not in {self.categories}') + +
+[docs] + def __init__(self, + name: str, + categories: str | list[str]): + super().__init__(name=name) + if isinstance(categories, str): + self.categories = categories.split(',') + else: + self.categories = categories + for c in categories: + self._validate_value(c)
+ + + def __repr__(self): + """String representation of object""" + return f"{self.__class__.__name__}(name={self.name}, categories={self.categories})"
+ + + +
+[docs] +class Param_Ordinal(Param_Discrete): + """ + Represents an ordinal parameter; a discrete parameter with an order. + """ + # Ordinal encoder maps to integers + +
+[docs] + def encode(self, X: np.ndarray): + """Encode parameter to a format that can be used for training""" + return self.unit_map(X)
+ + +
+[docs] + def decode(self, X: np.ndarray): + """Decode parameter from transformed space""" + return self.unit_demap(X)
+
+ + + +
+[docs] +class Param_Categorical(Param_Discrete): + """ + Represents an categorical parameter; a discrete parameter without an order. + """ + + # Categorical encoder maps to one-hot columns + # No decorator on this, we need to handle dataframes +
+[docs] + def encode(self, X: str | ArrayLike): + """Encode parameter to a format that can be used for training""" + X_str = np.array(X).flatten().astype('U11') + X_cat = pd.Series(X_str).astype(pd.CategoricalDtype(categories=self.categories)) + X_ohe = pd.get_dummies(X_cat, prefix_sep=CAT_SEP, dtype=float, prefix=self.name) + + return X_ohe
+ + +
+[docs] + def decode(self, X: ArrayLike): + """Decode parameter from transformed space""" + return [self.categories[int(x)] for x in np.array(X).argmax(axis=1)]
+
+ + + +
+[docs] +class Task(Param_Discrete): + """ + Represents an task parameter; a discrete parameter indicating a distinct system. + """ + # Similar to ordinal, but (0,nc-1) instead of (0,1) + + @transform_with_type + def encode(self, X: np.ndarray): + """Encode parameter to a format that can be used for training""" + return self.unit_map(X)*self.nc + + @transform_with_type + def decode(self, X: np.ndarray): + """Decode parameter from transformed space""" + return np.array(self.unit_demap(X/self.nc)).astype('U11')
+ + + +
+[docs] +class Param_Discrete_Numeric(Param_Discrete): + """ + Represents an discrete numeric parameter; an ordinal parameter comprised of numbers. + + Raises: + TypeError: If the categories are not numbers. + TypeError: If the categories are not a list of numbers. + """ + + @property + def min(self): + """Minimum parameter value""" + return np.array(self.categories).min() + + @property + def max(self): + """Maximum parameter value""" + return np.array(self.categories).max() + + @property + def range(self): + """The range of the parameter (max - min)""" + return self.max-self.min + +
+[docs] + def __init__(self, + name, + categories: int | float | list[int | float]): + if not isinstance(categories, (int, float, list)): + raise TypeError('Categories must be a number or list of numbers') + if isinstance(categories, list): + if not all(isinstance(x, (int, float)) for x in categories): + raise TypeError('Categories must be a list of numbers') + + self.name = name + self.categories = categories if isinstance(categories, list) else [categories] + self.categories.sort() + for c in categories: + self._validate_value(c)
+ + + def _validate_value(self, + value: int | float): + """ + Validates if the given value is in the list of categories. + + Args: + value (str): The value to be validated. + + Raises: + KeyError: If the value is not in the list of categories. + TypeError: If the value is not a string + """ + if not isinstance(value, (int, float)): + raise TypeError(f'Value {value} is not numeric') + + if value not in self.categories: + raise ValueError(f'Value {value} is not in {self.categories}') + + @transform_with_type + def unit_map(self, X: np.ndarray): + """Map from measured to 0,1 space""" + return (X-self.min)/self.range if self.range != 0 else 0*X + + @transform_with_type + def unit_demap(self, X: np.ndarray): + """Map from 0,1 to measured space""" + closest_idx = np.abs(np.array(self.categories) + - (X.flatten()[..., np.newaxis]*self.range+self.min)).argmin(axis=1) + return np.array(self.categories)[closest_idx].reshape(X.shape) + + encode = unit_map + decode = unit_demap
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/parameters/param_space.html b/_modules/obsidian/parameters/param_space.html new file mode 100644 index 0000000..ac3b0ae --- /dev/null +++ b/_modules/obsidian/parameters/param_space.html @@ -0,0 +1,797 @@ + + + + + + + + + + obsidian.parameters.param_space — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.parameters.param_space

+"""A collection of parameters jointly defining an operating or optimization space"""
+
+from .continuous import Parameter, Param_Continuous, Param_Observational
+from .discrete import (
+    Param_Categorical, Param_Ordinal,
+    Param_Discrete, Param_Discrete_Numeric,
+    Task, CAT_SEP
+)
+from abc import ABC
+import torch
+
+import numpy as np
+import pandas as pd
+
+from obsidian.exceptions import UnsupportedError
+
+
+
+[docs] +class ParamSpace(ABC): + """ + Class designed to define the parameter space in which an optimization + can be conducted. + + Attributes: + params (tuple[Parameter]): A tuple of Parameter types defining the parameter space. + X_names (tuple[str]): A tuple of the names of the parameters. + X_cont (list[str]): A list of the names of the continuous parameters. + X_obs (list[str]): A list of the names of the observational parameters. + X_discrete (dict[str, list[str]]): A dictionary mapping the names of the discrete parameters to their categories. + X_task (dict[str, list[str]]): A dictionary mapping the names of the task parameters to their categories. + X_min (torch.Tensor): A tensor containing the minimum values of the parameters. + X_max (torch.Tensor): A tensor containing the maximum values of the parameters. + X_range (torch.Tensor): A tensor containing the range of the parameters. + X_static (list[str]): A list of the names of the static parameters. + X_vary (list[str]): A list of the names of the varying parameters. + n_dim (int): The number of dimensions in the parameter space. + n_tdim (int): The total number of dimensions in the parameter space after transformation. + t_map (dict): A dictionary mapping parameter indices to transformed indices. + tinv_map (dict): A dictionary mapping transformed indices to parameter indices. + X_discrete_idx (list[int]): A list of the indices of the discrete parameters. + X_t_discrete_idx (list[int]): A list of the indices of the transformed discrete parameters. + X_t_cat_idx (list[int]): A list of the indices of the transformed categorical parameters. + X_t_task_idx (int): The index of the transformed task parameter. + + Raises: + ValueError: If the X_names are not unique. + UnsupportedError: If there is more than one task parameter. + + """ +
+[docs] + def __init__(self, + params: list[Parameter]): + + # Convert to immutable dtype to presever order + self.params = tuple(params) + self.X_names = tuple([x.name for x in self]) + if len(set(self.X_names)) != len(self.X_names): + raise ValueError("X_names must be unique.") + if any([CAT_SEP in x for x in self.X_names]): + raise ValueError(f"X_names cannot contain '{CAT_SEP}'.") + + # Save as attributes key types that affect usage behavior + self.X_cont = [x for x in self if isinstance(x, Param_Continuous) + and not isinstance(x, Param_Observational)] + self.X_obs = [x for x in self if isinstance(x, Param_Observational)] + self.X_discrete = [x for x in self if isinstance(x, Param_Discrete)] + self.X_task = [x for x in self if isinstance(x, Task)] + if len(self.X_task) > 1: + raise UnsupportedError("Only one task parameter is allowed.") + + # Locally calculate other types for counting + + X_cat = [x for x in self if isinstance(x, Param_Categorical)] + X_ord = [x for x in self if isinstance(x, Param_Ordinal)] + X_disc_num = [x for x in self if isinstance(x, Param_Discrete_Numeric)] + + # Extract ranges for scaling + self.X_min = torch.Tensor([x.min for x in self]) + self.X_max = torch.Tensor([x.max for x in self]) + self.X_range = self.X_max - self.X_min + + # Extract where variables are changing or are fixed/observational + self.X_static = [x.name for x in self if x.min == x.max or isinstance(x, Param_Observational)] + self.X_vary = [name for name in self.X_names if name not in self.X_static] + + # Extract number of dimensions (raw input and encoded) + self.n_dim = len(self.X_names) + # Total number of dimensions after transform + # Numerical + Observational + Unique Categories + Ordinal + Discrete_Numeric + self.n_tdim = int(len(self.X_cont) + + len(self.X_obs) + + np.sum([len(x.categories) for x in X_cat]) + + len(X_ord) + + len(X_disc_num)) + + # Assign the inv/transform maps + self.t_map = self.map_transform() + self.tinv_map = self.map_inv_transform() + + # Save the locations of discrete variables + self.X_discrete_idx = [i for i in self.t_map.keys() if self[i] in self.X_discrete] + self.X_t_discrete_idx = [i_t for i_t, i in self.tinv_map.items() if self[i] in self.X_discrete] + self.X_t_cat_idx = [i_t for i_t, i in self.tinv_map.items() if self[i] in X_cat] + if self.X_task: + self.X_t_task_idx = next(i_t for i_t, i in self.tinv_map.items() if self[i] in self.X_task) + else: + self.X_t_task_idx = None + + return
+ + + def __iter__(self): + """Iterate over the parameters in the parameter space""" + return iter(self.params) + + def __len__(self): + """Number of parameters in the parameter space""" + return len(self.params) + + def __repr__(self): + """String representation of object""" + return f"{self.__class__.__name__}(params={[p.name for p in self]})" + + def __getitem__(self, index: int) -> Parameter: + """Retrieve a parameter by index""" + return self.params[index] + +
+[docs] + def map_transform(self) -> dict: + """ + Maps the parameter indices to transformed indices based on the parameter types. + + Returns: + dict: A dictionary mapping parameter indices to transformed indices. + """ + t_map = {} + count = 0 + for i, param_i in enumerate(self.params): + if isinstance(param_i, Param_Categorical): + t_map[i] = [count + j for j in range(len(param_i.categories))] + count += len(param_i.categories) - 1 + else: + t_map[i] = count + count += 1 + + return t_map
+ + +
+[docs] + def map_inv_transform(self) -> dict: + """ + Maps the inverse of the transformed dictionary. + + Returns: + dict: A dictionary where the keys are the original values and the values are the original keys. + """ + t_map = self.map_transform() + tinv_map = {} + for k, v in t_map.items(): + if isinstance(v, list): + for i in v: + tinv_map[i] = k + else: + tinv_map[v] = k + + return tinv_map
+ + + # Establish a handler method to transform X based on type + def _transform(self, + X: pd.DataFrame, + type: str) -> pd.DataFrame: + """ + Transforms the input DataFrame `X` based on the specified `type`. + + Args: + X (pd.DataFrame): The input DataFrame to be transformed. + type (str): The type of transformation to be applied. + + Returns: + pd.DataFrame: The transformed DataFrame. + + Raises: + KeyError: If an unknown column is passed in `X`. + """ + cols = X.columns + # Check for column match, but consider that categorical OH encoding will lead to Parameter N_i type names + if not all(col.split(CAT_SEP)[0] in self.X_names for col in cols): + raise KeyError('Unknown column passed in X') + X_t = pd.DataFrame() + for param_i in self: + methods = {'unit_map': param_i.unit_map, + 'unit_demap': param_i.unit_demap, + 'encode': param_i.encode, + 'decode': param_i.decode} + # Include all matches, including OH columns (e.g. Parameter N_i) + cols = [col for col in X.columns if param_i.name in col] + # Skip if one of the possible parameters is excluded from the transform operation + if cols != []: + X_t_i = pd.DataFrame(methods[type](X[cols].values)) + X_t_i.columns = [param_i.name] if not ( + isinstance(param_i, Param_Categorical) and type == 'encode') else X_t_i.columns + X_t = pd.concat([X_t, X_t_i], axis=1) + + if type in ['encode', 'unit_map']: + X_t = X_t.apply(pd.to_numeric) # Do not send object dtypes for these transforms + + return X_t + + # Define transformation methods using a handler method and lambdas; more readable on outer layer +
+[docs] + def unit_map(self, X): + """Map from measured to 0,1 space""" + return self._transform(X, type='unit_map')
+ + +
+[docs] + def unit_demap(self, X): + """Map from 0,1 space to measured space""" + return self._transform(X, type='unit_demap')
+ + +
+[docs] + def encode(self, X): + """Encode parameter to a format that can be used for training""" + return self._transform(X, type='encode')
+ + +
+[docs] + def decode(self, X): + """Decode parameter from transformed space""" + return self._transform(X, type='decode')
+ + +
+[docs] + def save_state(self) -> dict: + """ + Saves the state of the ParamSpace object. + + Returns: + dict: A dictionary containing the state of the ParamSpace object. + """ + obj_dict = {} + for param in self: + obj_dict[param.name] = {'state': param.save_state(), 'class': param.__class__.__name__} + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, + obj_dict: dict): + """ + Loads the state of the ParamSpace object from a dictionary. + + Args: + obj_dict (dict): A dictionary containing the state of the ParamSpace object. + + Returns: + ParamSpace: A new ParamSpace object with the loaded state. + + """ + param_type_dict = {'Param_Continuous': Param_Continuous, + 'Param_Categorical': Param_Categorical, + 'Param_Ordinal': Param_Ordinal, + 'Param_Discrete_Numeric': Param_Discrete_Numeric, + 'Param_Observational': Param_Observational, + 'Task': Task} + params = [] + for param, param_dict in obj_dict.items(): + param = param_type_dict[param_dict['class']].load_state(param_dict['state']) + params.append(param) + return cls(params=params)
+ + +
+[docs] + def mean(self) -> pd.DataFrame: + """ + Calculates the mean values for each parameter in the parameter space. + + Returns: + pd.DataFrame: A DataFrame containing the mean values for each parameter. + """ + row = {} + for param_i in self: + if isinstance(param_i, Param_Continuous): # Mean of continuous + row[param_i.name] = param_i.unit_demap([0.5])[0] + elif isinstance(param_i, Param_Discrete): # First of discrete + row[param_i.name] = param_i.categories[0] + + df_mean = pd.DataFrame([row]) + + return df_mean
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/parameters/targets.html b/_modules/obsidian/parameters/targets.html new file mode 100644 index 0000000..5ec65aa --- /dev/null +++ b/_modules/obsidian/parameters/targets.html @@ -0,0 +1,641 @@ + + + + + + + + + + obsidian.parameters.targets — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.parameters.targets

+"""Output features to be optimized"""
+
+import pandas as pd
+import numpy as np
+import torch
+from .transforms import f_transform_dict
+from numpy.typing import ArrayLike
+
+from obsidian.exceptions import UnfitError
+
+
+
+[docs] +class Target(): + """ + Base class for optimization response targets. + """ + +
+[docs] + def __init__(self, + name: str, + f_transform: str | None = 'Standard', + aim: str = 'max'): + + self.name = name + if aim not in ['min', 'max']: + raise ValueError('Aim must be either "min" or "max"') + if aim == 'min': + self.multiplier = -1 + else: + self.multiplier = 1 + self.aim = aim + + # Ouput scoring, used for transformation OR to create a cost function of multiple outputs/inputs + if f_transform is not None: + if f_transform not in f_transform_dict.keys(): + raise KeyError(f'Scoring function must be selected from one of: {f_transform_dict.keys()}') + else: + f_transform = 'Identity' + self.f_transform = f_transform
+ + + def __repr__(self): + """String representation of object""" + return f"{self.__class__.__name__}({self.name}, aim={self.aim})" + +
+[docs] + def transform_f(self, + f: float | int | ArrayLike, + inverse=False, + fit=False): + """ + Converts a raw response to an objective function value ("score"). + Cost-penalization and response transformation should be handled here. + + Args: + f (array-like): The column(s) containing the response values (y) + inverse (bool, optional): An indicator to perform the inverse transform. Defaults to ``False``. + fit (bool, optional): An indicator to fit the properties of the transform function. Defaults to ``False``. + + Returns: + pd.Series: An array of transformed f values matching the responses in Z + + Raises: + TypeError: If f is not numeric or array-like + UnfitError: If the transform function is called without being fit first + """ + + if not (isinstance(f, (pd.Series, pd.DataFrame, np.ndarray, list, float, int)) + or torch.is_tensor(f)): + raise TypeError('f being transformed must be numeric or array-like') + + if not fit: + if not hasattr(self, 'f_transform_func'): + raise UnfitError('Transform function is being called without being fit first.') + + if isinstance(f, (float, int)): + f = [f] + + if isinstance(f, (pd.Series, pd.DataFrame)): + f = f.values + + if not torch.is_tensor(f): + f = torch.tensor(f) + + if f.ndim == 1: + f = f.reshape(-1, 1) + + if inverse: + f_obj = self.f_transform_func.inverse(f) + return pd.Series(f_obj.flatten(), name=self.name) * self.multiplier + else: + if fit: + self.f_transform_func = f_transform_dict[self.f_transform]() + f_obj = self.f_transform_func(f, fit=True) + self.f_raw = f # Save raw data for re-loading and re-fitting state as needed + else: + f_obj = self.f_transform_func(f) + return pd.Series(f_obj.flatten(), name=self.name+' Trans') * self.multiplier
+ + +
+[docs] + def save_state(self) -> dict: + """ + Saves the state of the object as a dictionary. + + Returns: + dict: A dictionary containing the state of the object. + """ + # Prepare a dictionary to describe the state + obj_dict = {'init_attrs': {}} + + # Select some optimizer attributes to save directly + init_attrs = ['name', 'aim', 'f_transform'] + for attr in init_attrs: + obj_dict['init_attrs'][attr] = getattr(self, attr) + + # If the transformer has been fit, store the raw data so it can be refit upon load + if hasattr(self, 'f_transform_func'): + obj_dict['f_raw'] = self.f_raw.tolist() + + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, obj_dict: dict): + """ + Loads the state of the target object from a dictionary. + + Args: + cls (class): The class of the target object. + obj_dict (dict): A dictionary containing the state of the target object. + + Returns: + The loaded target object. + """ + new_target = cls(**obj_dict['init_attrs']) + + # If the transformer has been fit before saving, refit it + f = torch.Tensor(obj_dict['f_raw']) + new_target.transform_f(f, fit=True) + + return new_target
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/parameters/transforms.html b/_modules/obsidian/parameters/transforms.html new file mode 100644 index 0000000..74ba368 --- /dev/null +++ b/_modules/obsidian/parameters/transforms.html @@ -0,0 +1,686 @@ + + + + + + + + + + obsidian.parameters.transforms — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.parameters.transforms

+"""Transformation functions to normalize output responses"""
+
+from torch import logit, sigmoid
+from abc import ABC, abstractmethod
+import warnings
+from torch import Tensor
+
+from obsidian.exceptions import UnfitError
+
+# Method name pointers
+f_transform_dict = {'Standard': lambda: Standard_Scaler(),
+                    'Identity': lambda: Identity_Scaler(),
+                    'Logit_MinMax': lambda: Logit_Scaler(),
+                    'Logit_Percentage': lambda: Logit_Scaler(range_response=100, override_fit=True),
+                    }
+
+
+
+[docs] +class Target_Transform(ABC): + """ + Base class for obsidian Target transforms + """ +
+[docs] + def __init__(self): + self.params = {}
+ + + def _validate_fit(self): + """ + Validates if all parameters have been fit before transforming. + + Raises: + UnfitError: If any parameter value is None, indicating that the parameters have not been fit. + """ + if not all([(v is not None) for v in self.params.values()]): + raise UnfitError('Params must be fit before transforming.') + +
+[docs] + @abstractmethod + def forward(self, + X: Tensor, + fit: bool = False): + """Evaluate the forward transformation on input data X""" + pass # pragma: no cover
+ + +
+[docs] + @abstractmethod + def inverse(self, + X: Tensor): + """Inverse transform the transformed data X_t""" + pass # pragma: no cover
+ + + def __call__(self, + X: Tensor, + fit: bool = False): + """Shortcut to forward method""" + return self.forward(X, fit)
+ + + +
+[docs] +class Identity_Scaler(Target_Transform): + """ + Dummy scaler class which simply returns the input + """ +
+[docs] + def forward(self, + X: Tensor, + fit: bool = False): + """Evaluate the forward transformation on input data X""" + return X
+ + +
+[docs] + def inverse(self, + X: Tensor): + """Inverse transform the transformed data X_t""" + return X
+
+ + + +
+[docs] +class Standard_Scaler(Target_Transform): + """ + Scaler which normalizes based on zero mean and unit st-dev + """ +
+[docs] + def __init__(self): + self.params = {'mu': None, 'sd': None}
+ + +
+[docs] + def forward(self, + X: Tensor, + fit: bool = False): + """Evaluate the forward transformation on input data X""" + if fit: + X_v = X[~X.isnan()] + self.params = {'mu': X_v.mean(), 'sd': X_v.std()} + else: + self._validate_fit() + return (X-self.params['mu'])/self.params['sd']
+ + +
+[docs] + def inverse(self, X): + """Inverse transform the transformed data X_t""" + self._validate_fit() + return X*self.params['sd']+self.params['mu']
+
+ + + +
+[docs] +class Logit_Scaler(Target_Transform): + """ + Scaler which normalizes based on a logit transform + Can be fit to select an appropriate range for the logit + """ +
+[docs] + def __init__(self, + range_response: int | float = 1, + loc: int | float = 0, + override_fit: bool = False, + standardize: bool = True): + self.params = {'scale': 1/range_response, 'loc': loc, 'mu': None, 'sd': None} + # Override "fitting" when valid ranges are provided during init + self.override_fit = override_fit + self.standardize = standardize
+ + + def _fit_minmax(self, + X: Tensor): + """Fits the min-max scale of the logit transform""" + # Scale X into a range from 0-1 with buffer/2 on either side + self.override_fit = False + range_response = X.max()-X.min() + buffer = 0.2 + self.params['scale'] = (1-buffer)/range_response + self.params['loc'] = X.min() - (buffer/2)*(1/self.params['scale']) + +
+[docs] + def forward(self, + X: Tensor, + fit: bool = False): + """Evaluate the forward transformation on input data X""" + # If fit is not called, and the range is valid, transform + # If the range is invalid, fit the range first and warn + if not fit or self.override_fit: + X_s = self.params['scale']*(X - self.params['loc']) + valid_range = (X_s >= 0).all() and (X_s <= 1).all() + if not valid_range: + warnings.warn('Invalid range provided for logit scaler, proceeding with min-max fit') + self._fit_minmax(X) + return self.forward(X) + else: + X_v = X[~X.isnan()] + self._fit_minmax(X_v) + X_s = self.params['scale']*(X - self.params['loc']) + X_st = logit(X_s) + if self.standardize: + self.params.update({'mu': X_st.mean(), 'sd': X_st.std()}) + return (X_st-self.params['mu'])/self.params['sd'] + else: + return X_st
+ + +
+[docs] + def inverse(self, + X: Tensor): + """Inverse transform the transformed data X_t""" + if self.standardize: + self._validate_fit() + X = X*self.params['sd']+self.params['mu'] + return (1/self.params['scale'])*sigmoid(X)+self.params['loc']
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/parameters/utils.html b/_modules/obsidian/parameters/utils.html new file mode 100644 index 0000000..954dc7a --- /dev/null +++ b/_modules/obsidian/parameters/utils.html @@ -0,0 +1,509 @@ + + + + + + + + + + obsidian.parameters.utils — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.parameters.utils

+"""Utility functions for parameter handling"""
+
+import numpy as np
+
+
+
+[docs] +def transform_with_type(inner): + """ + Wraps parameter transform functions (map, encode, etc) + in a way that returns the same type that was given + """ + def wrapper(self, X): + X_arr = np.array(X) + X_t = inner(self, X_arr) + return X_t.tolist() if isinstance(X, (list, int, float, str)) else X_t + return wrapper
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/plotting/mpl.html b/_modules/obsidian/plotting/mpl.html new file mode 100644 index 0000000..09af68e --- /dev/null +++ b/_modules/obsidian/plotting/mpl.html @@ -0,0 +1,537 @@ + + + + + + + + + + obsidian.plotting.mpl — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.plotting.mpl

+import matplotlib.pyplot as plt
+import matplotlib.gridspec as gridspec
+from matplotlib.pyplot import Figure
+from obsidian.campaign import Campaign
+import numpy as np
+
+
+
+[docs] +def visualize_inputs(campaign: Campaign) -> Figure: + """ + Visualizes the input variables of a campaign. + + Args: + campaign (Campaign): The campaign object containing the input data. + + Returns: + Figure: The matplotlib Figure object containing the visualization. + """ + n_dim = campaign.X_space.n_dim + + fig = plt.figure(constrained_layout=True, figsize=(2*(n_dim+1), 2.5)) + gs = gridspec.GridSpec(ncols=n_dim+1, nrows=1, figure=fig) + + X0 = campaign.data[list(campaign.X_space.X_names)] + X_u = campaign.X_space.unit_map(X0) + m_exp = X0.shape[0] + + for i, param in enumerate(X0.columns): + ax = fig.add_subplot(gs[0, i]) + ax.plot(X0[param], 'o') + ax.set_xlabel('Experiment') + ax.set_title(param) + ax.set_xticks(np.around(np.linspace(0, m_exp, 5))) + + ax = fig.add_subplot(gs[0, -1]) + plt.imshow(abs(X_u.corr()), cmap='viridis') + ax.set_xticks(range(n_dim)) + ax.set_yticks(range(n_dim)) + ax.set_xticklabels(X0.columns, rotation=90, ha='right') + ax.set_yticklabels(X0.columns) + plt.colorbar() + plt.title('Correlation Plot') + + return fig
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/plotting/plotly.html b/_modules/obsidian/plotting/plotly.html new file mode 100644 index 0000000..1090ccb --- /dev/null +++ b/_modules/obsidian/plotting/plotly.html @@ -0,0 +1,941 @@ + + + + + + + + + + obsidian.plotting.plotly — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.plotting.plotly

+"""Plotly figure-generating functions"""
+
+from obsidian.campaign import Campaign
+from obsidian.optimizer import Optimizer
+from obsidian.exceptions import UnfitError, UnsupportedError
+from .branding import obsidian_colors
+
+import plotly.graph_objects as go
+from plotly.graph_objects import Figure
+
+import pandas as pd
+import numpy as np
+
+
+
+[docs] +def parity_plot(optimizer: Optimizer, + f_transform: bool = False, + response_id: int = 0) -> Figure: + """ + Produces a plot of surrogate model predictions at an OFAT range in one variable. + + Args: + optimizer (Optimizer): The optimizer object which contains a surrogate that has been fit to data + and can be used to make predictions. + f_transform (bool, optional): An indicator for whether or not to plot the response value in the "objective + function" form which is directly used by the optimizer, else using the "measured response" form which the + optimizer preproceses. Default value is ``False`` which plots the raw measured response scale. + response_id (int, optional): Index of the response for potential multi-response models. + Default value is ``0`` (single-response). + + Returns: + fig (Figure): The optimizer fit parity plot + + Raises: + TypeError: If the optimizer is not an instance of obsidian Optimizer + UnfitError: If the optimizer is not fit + ValueError: If the response_id is not a valid response index + """ + + if not isinstance(optimizer, Optimizer): + raise TypeError('Optimizer must be an instance of obsidian Optimizer') + if not optimizer.is_fit: + raise UnfitError('Optimizer must be fit before plotting predictions') + if response_id < 0 or response_id >= len(optimizer.target): + raise ValueError('response_id must be a valid response index') + + X = optimizer.X_train + y_pred = optimizer.predict(X, return_f_inv=not f_transform) + y_true = optimizer.f_train if f_transform else optimizer.y_train + + y_name = optimizer.y_names[response_id] + + y_lb = y_pred[y_name+' lb'].values + y_ub = y_pred[y_name+' ub'].values + y_pred = y_pred[y_name+' (pred)'].values + y_true = y_true[y_name].values + + RMSE = ((y_true-y_pred)/y_true)**2 + + y_min = np.min([y_true.min(), y_pred.min()]) + y_max = np.max([y_true.max(), y_pred.max()]) + abs_margin = 0.1 + y_abs = [y_min/(1+abs_margin), y_max*(1+abs_margin)] + + fig = go.Figure() + + fig.add_trace(go.Scatter(x=y_true, y=y_pred, + error_y={'array': y_ub - y_pred, + 'arrayminus': y_pred - y_lb, + 'color': 'gray', 'thickness': 0.5}, + mode='markers', + name='Observations', + marker={'color': RMSE, 'colorscale': 'Viridis', 'size': 15}, + )) + + fig.add_trace(go.Scatter(x=y_abs, y=y_abs, + mode='lines', + name='Parity', + showlegend=False, + line={'color': 'black', 'dash': 'dot'})) + + fig.update_xaxes(title_text=f'Actual Response ({y_name})') + fig.update_yaxes(title_text=f'Predicted Response ({y_name})') + fig.update_layout(template='ggplot2', title='Parity Plot', + autosize=False, height=400, width=600) + + return fig
+ + + +
+[docs] +def factor_plot(optimizer: Optimizer, + feature_id: int = 0, + response_id: int = 0, + f_transform: bool = False, + X_ref: pd.DataFrame | None = None, plotRef: bool = True, + ylim: tuple[float, float] | None = None) -> Figure: + """ + Produces a plot of surrogate model predictions at an OFAT range in one variable. + + Args: + optimizer (Optimizer): The optimizer object which contains a surrogate that has been fit to data + and can be used to make predictions. + feature_id (int, optional): The index of the desired variable to plot from the last data used + to fit the surrogate model. The default value is ``0``. + response_id (int, optional): Index of the response for potential multi-response models. + Default value is ``0`` (single-response). + f_transform (bool, optional): An indicator for whether or not to plot the response value in the "objective + function" form which is directly used by the optimizer, else using the + "measured response" form which the optimizer preproceses. Default value is + ``False`` which plots the raw measured response scale. + plotRef (bool, optional): An indicator for whether or not to plot the reference data points. + Default value is ``True``. + ylim (tuple, optional): The y-axis limits for the plot. Default value is ``None``. + + Returns: + fig (Figure): The matplotlib plot of response value versus 1 predictor variable. + + Raises: + TypeError: If the optimizer is not an instance of obsidian Optimizer + UnfitError: If the optimizer is not fit + ValueError: If the feature_id is not a valid feature index + ValueError: If the response_id is not a valid response index + ValueError: If X_ref is provided and not a pd.Series + """ + if not isinstance(optimizer, Optimizer): + raise TypeError('Optimizer must be an instance of obsidian Optimizer') + if not optimizer.is_fit: + raise UnfitError('Optimizer must be fit before plotting predictions') + if feature_id >= len(optimizer.X_space): + raise ValueError('feature_id must be a valid feature index') + if response_id < 0 or response_id >= len(optimizer.target): + raise ValueError('response_id must be a valid response index') + + # Create a dataframe of test samples for plotting + n_samples = 100 + if X_ref is None: + df_mean = optimizer.X_space.mean() + X_test = pd.concat([df_mean]*n_samples, axis=0).reset_index(drop=True) + else: + if not isinstance(X_ref, pd.DataFrame): + raise TypeError('X_ref must be a DataFrame') + X_test = pd.concat([X_ref]*n_samples, axis=0).reset_index(drop=True) + + # Vary the indicated column + X_name = X_test.columns[feature_id] + param_i = optimizer.X_space.params[feature_id] + unit_span = np.linspace(0, 1-1e-15, n_samples) # Prevent issues with cat[1.0] = undefined + X_test[X_name] = param_i.unit_demap(unit_span) + X = X_test[X_name].values + + Y_pred = optimizer.predict(X_test, return_f_inv=not f_transform, PI_range=0.95) + y_name = optimizer.y_names[response_id] + + Y_mu = Y_pred[y_name+('_t (pred)' if f_transform else ' (pred)')].values + LCB = Y_pred[y_name+('_t lb' if f_transform else ' lb')].values + UCB = Y_pred[y_name+('_t ub' if f_transform else ' ub')].values + + fig = go.Figure() + + fig.add_trace(go.Scatter(x=np.append(X, X[::-1]), y=np.append(UCB, LCB[::-1]), + fill='toself', + opacity=0.3, + line={'color': obsidian_colors.teal}, + showlegend=True, + name='95% Pred Band'), + ) + + fig.add_trace(go.Scatter(x=X, y=Y_mu, + mode='lines', + line={'color': obsidian_colors.teal}, + name='Mean'), + ) + if (X_ref is not None) and plotRef: + Y_pred_ref = optimizer.predict(X_ref, return_f_inv=not f_transform) + Y_mu_ref = Y_pred_ref[y_name+('_t (pred)' if f_transform else ' (pred)')].values + fig.add_trace(go.Scatter(x=X_ref.iloc[:, feature_id].values, y=Y_mu_ref, + mode='markers', + line={'color': obsidian_colors.teal}, + name='Ref'), + ) + fig.update_xaxes(title_text=X_name) + fig.update_yaxes(title_text=y_name) + fig.update_layout(template='ggplot2', title=f'Factor Effect Plot for {X_name}') + fig.update_layout(autosize=False, width=600, height=400) + if ylim is not None: + fig.update_layout(yaxis_range=ylim) + + return fig
+ + + +
+[docs] +def surface_plot(optimizer: Optimizer, + feature_ids: list[int, int] = [0, 1], + response_id: int = 0, + f_transform: bool = False, + plot_bands: bool = True, + plot_data: bool = False) -> Figure: + """ + Produces a surface plot of surrogate model predictions over a 2-parameter grid range. + + Args: + optimizer (Optimizer): The optimizer object which contains a surrogate that has been fit to data + and can be used to make predictions. + feature_ids (list, optional): A list of integers containing the indices of the desired variables + to plot from the last data used to fit the surrogate model. Default value is ``[0,1]``. + f_transform (bool, optional): An indicator for whether or not to plot the response value in the "objective + function" form which is directly used by the optimizer, else using the + "measured response" form which the optimizer preprocesses. Default value is + ``False`` which plots the raw measured response scale. + plot_bands (bool, optional): An indicator for whether or not to plot the confidence bands as a wire + frame around the surface plot. Default is ``True``. + plot_data (bool, optional): An indicator for whether or not to plot the raw data locations. + Default is ``False``, as the data z-height can be misleading for >2D data on a 3D plot. + response_id (int, optional): Index of the response for potential multi-response models. + Default value is ``0`` (single-response). + + Returns: + fig (Figure): The matplotlib plot of surfaces over a 2-parameter grid. + + Raises: + TypeError: If the optimizer is not an instance of obsidian Optimizer + UnfitError: If the optimizer is not fit + ValueError: If the feature_ids are not valid feature indices + ValueError: If the response_id is not a valid response index + """ + if not isinstance(optimizer, Optimizer): + raise TypeError('Optimizer must be an instance of obsidian Optimizer') + for feature_id in feature_ids: + if feature_id >= len(optimizer.X_space): + raise ValueError('feature_id must be a valid feature index') + if not optimizer.is_fit: + raise UnfitError('Optimizer must be fit before plotting predictions') + if response_id < 0 or response_id >= len(optimizer.target): + raise ValueError('response_id must be a valid response index') + if plot_data and f_transform: + raise UnsupportedError('Plotting data is not supported for transformed responses') + + # Create a dataframe of test samples for plotting + n_grid = 100 + df_mean = optimizer.X_space.mean() + X_test = pd.concat([df_mean]*(n_grid**2), axis=0).reset_index(drop=True) + + # Create a mesh grid which is necessary for the 3D plot + X0_name = X_test.columns[feature_ids[0]] + X1_name = X_test.columns[feature_ids[1]] + + # Vary the indicated column + x_axes = [] + unit_span = np.linspace(0, 1-1e-15, n_grid) + + params = [optimizer.X_space.params[i] for i in feature_ids] + for param_i in params: + x_axes.append(np.array(param_i.unit_demap(unit_span))) + + x0_ax, x1_ax = x_axes + X0, X1 = np.meshgrid(x0_ax, x1_ax) + X_test[X0_name] = X0.flatten().reshape(-1, 1) + X_test[X1_name] = X1.flatten().reshape(-1, 1) + + Y_pred = optimizer.predict(X_test, return_f_inv=not f_transform, PI_range=0.95) + y_name = optimizer.y_names[response_id] + + Y_mu = Y_pred[y_name+('_t (pred)' if f_transform else ' (pred)')].values + LCB = Y_pred[y_name+('_t lb' if f_transform else ' lb')].values + UCB = Y_pred[y_name+('_t ub' if f_transform else ' ub')].values + + Z_mu = Y_mu.reshape(n_grid, n_grid) + Z_LCB = LCB.reshape(n_grid, n_grid) + Z_UCB = UCB.reshape(n_grid, n_grid) + + fig = go.Figure() + fig.add_trace(go.Surface(z=Z_mu, x=x0_ax, y=x1_ax, + name='Mean', + opacity=0.85)) + + # Plot the confidence bands only if desired + if plot_bands: + for i, (x0, x1, ucb, lcb) in enumerate(zip(X0, X1, Z_UCB, Z_LCB)): + fig.add_trace(go.Scatter3d(z=ucb, x=x0, y=x1, + mode='lines', + line={'color': '#b0b0b0', 'width': 2}, + opacity=0.5, + name='UCB', + legendgroup='UCB', + showlegend=True if i == 0 else False), + ) + fig.add_trace(go.Scatter3d(z=lcb, x=x0, y=x1, + mode='lines', + line={'color': '#b0b0b0', 'width': 2}, + opacity=0.5, + name='LCB', + legendgroup='LCB', + showlegend=True if i == 0 else False), + ) + + fig.update_layout(legend_orientation='h', template='ggplot2', title=f'Surface Plot for {X0_name} vs {X1_name}', + scene=dict(xaxis_title=X0_name, yaxis_title=X1_name, zaxis_title=y_name)) + + Z_train = optimizer.f_train if f_transform else optimizer.y_train + + # Plot the locations of raw data only if desired + if plot_data: + # Use a marker size which is larger if closer to the "other" values of the surface + # Smaller markers mean there are other variables pulling it away from the surface plot + if len(optimizer.X_space) != 2: + X_t_train_ex = optimizer.X_t_train.copy().drop(columns=[X0_name, X1_name]).values + X_t_test_ex = optimizer.X_space.encode(X_test.copy().drop(columns=[X0_name, X1_name]).drop_duplicates()).values + + if X_t_train_ex.shape[0] > 1: + X_t_dist = np.linalg.norm(X_t_train_ex-X_t_test_ex, ord=2, axis=1) + X_t_dist_scaled = (X_t_dist - X_t_dist.min())/(X_t_dist.max()-X_t_dist.min()) + dist_scaled = 15-10*X_t_dist_scaled + else: + dist_scaled = 10 + else: + dist_scaled = 10 + + fig.add_trace(go.Scatter3d(x=optimizer.X_train[X0_name], y=optimizer.X_train[X1_name], + z=Z_train[y_name], + mode='markers', + marker={'color': '#000000', 'size': dist_scaled}, + name='Observations')) + + return fig
+ + + +
+[docs] +def MOO_results(campaign: Campaign, + response_ids: list[int] = [0, 1], + color_feature_id: int | None = None, + y_suggest: pd.DataFrame | None = None) -> Figure: + """ + Generates a plotly figure to visualize multi-objective optimization (MOO) results. + + Args: + campaign (Campaign): The campaign object containing the data. + response_ids (list[int], optional): The indices of the responses to plot. Defaults to ``[0, 1]``. + color_feature_id (int | None, optional): The index of the feature to use for coloring the markers. + Defaults to ``None``. + y_suggest (pd.DataFrame | None, optional): The suggested data for the responses. + Defaults to ``None``. + + Returns: + Figure: The plotly figure. + + Raises: + ValueError: If the campaign has less than two responses. + ValueError: If the response ID is out of range. + ValueError: If the color feature ID is out of range. + ValueError: If the suggested data does not contain all responses. + """ + fig = go.Figure() + + if not campaign._is_moo: + raise ValueError('Campaign must have at least two responses for MOO results') + for id in response_ids: + if id >= campaign.n_response: + raise ValueError(f'Response ID {id} is out of range') + if color_feature_id is not None: + if color_feature_id >= len(campaign.X_space): + raise ValueError(f'Color feature ID {color_feature_id} is out of range') + + response_0 = campaign.y_names[response_ids[0]] + response_1 = campaign.y_names[response_ids[1]] + X_names = list(campaign.X_space.X_names) + + yexp_0 = campaign.data[response_0] + yexp_1 = campaign.data[response_1] + + if color_feature_id is not None: + x_color_name = campaign.X_space.X_names[color_feature_id] + x_color = campaign.data[x_color_name] + marker_dict = dict(color=x_color, + colorscale=[[0, obsidian_colors.rich_blue], + [0.5, obsidian_colors.teal], + [1, obsidian_colors.lemon]], + showscale=True, + colorbar=dict(title=x_color_name)) + + else: + x_color = None + marker_dict = dict(color=obsidian_colors.primary.teal) + + fig.add_trace(go.Scatter( + x=yexp_0, y=yexp_1, + mode='markers', + marker=marker_dict, + customdata=campaign.data[X_names], + name='Data')) + + template = ["<b>"+str(name)+": "+" %{customdata["+str(i)+"]:.3G}</b><br>" + for i, name in enumerate(X_names)] + fig.update_traces(hovertemplate=''.join(template)) + + if y_suggest is not None: + if not all(y+' (pred)' in y_suggest.columns for y in campaign.y_names): + raise ValueError('Suggested data must contain all responses') + + y_0 = y_suggest[response_0 + ' (pred)'] + y_1 = y_suggest[response_1 + ' (pred)'] + lb_0 = y_suggest[response_0 + ' lb'] + ub_0 = y_suggest[response_0 + ' ub'] + lb_1 = y_suggest[response_1 + ' lb'] + ub_1 = y_suggest[response_1 + ' ub'] + + fig.add_trace(go.Scatter( + x=y_0, + y=y_1, + mode='markers', + marker=dict(color=obsidian_colors.accent.pastel_blue, + symbol='diamond-open', + size=7, + line=dict(width=2)), + name='Suggested', + error_y={'array': ub_1 - y_1, + 'arrayminus': y_1 - lb_1, + 'color': 'gray', 'thickness': 1}, + error_x={'array': ub_0 - y_0, + 'arrayminus': y_0 - lb_0, + 'color': 'gray', 'thickness': 1}, + hovertemplate=response_0 + ' :%{x:.3G} +%{error_x.array:.2G}/- \ + %{error_x.arrayminus:.2G}<br>' + response_1 + ': %{y:.3G} \ + +%{error_y.array:.2G}/-%{error_y.arrayminus:.2G}')) + + fig.update_layout( + xaxis_title=response_0, + yaxis_title=response_1, + title='Optimization Results' + ) + + fig.update_layout(coloraxis_colorbar=dict(yanchor="top", y=1, x=0, ticks="outside")) + + fig.update_layout(legend=dict( + yanchor="bottom", + y=0.05, + xanchor="right", + x=0.95 + )) + + fig.update_layout(coloraxis_colorbar_title_text='your title') + fig.update_layout(width=500, height=400, template='ggplot2') + return fig
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/plotting/shap.html b/_modules/obsidian/plotting/shap.html new file mode 100644 index 0000000..b45b086 --- /dev/null +++ b/_modules/obsidian/plotting/shap.html @@ -0,0 +1,812 @@ + + + + + + + + + + obsidian.plotting.shap — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.plotting.shap

+import matplotlib.pyplot as plt
+import numpy as np
+import pandas as pd
+
+from shap import Explanation
+from shap.plots.colors import blue_rgb, light_blue_rgb, red_blue_transparent, red_rgb
+from shap.utils import convert_name
+from shap.plots._partial_dependence import compute_bounds
+
+from matplotlib.pyplot import get_cmap
+
+
+
+[docs] +def one_shap_value(shap_value_new, expected_value, X_names): + """ + Visualize the shap values of one data point + """ + + cumulative_shap = np.cumsum(shap_value_new) + pred_new = expected_value + np.sum(shap_value_new) + + fig1 = plt.figure(figsize=(8, 6)) + plt.bar(range(len(shap_value_new)), shap_value_new) + + plt.xticks(range(len(shap_value_new)), X_names) + plt.ylabel('SHAP Value') + plt.xlabel('Feature') + plt.title('SHAP Values for Single Datapoint') + plt.axhline(0, color='grey', linestyle='--') + plt.close(fig1) + + fig2 = plt.figure(figsize=(8, 6)) + + y = cumulative_shap+expected_value + n = len(cumulative_shap) + + plt.plot(range(n), y, color = 'steelblue') + plt.scatter(range(n), y, color='blue') + + plt.vlines(0, expected_value, y[0], colors='steelblue', linestyles='solid') + plt.scatter(0, expected_value, color='purple') + plt.scatter(n-1, y[-1], color='pink') + + plt.xticks(range(n), X_names) + plt.xlabel('Feature') + plt.ylabel('Cumulative SHAP Value') + plt.title('Cumulative SHAP Values for Single Datapoint') + plt.axhline(expected_value, color='purple', linestyle='--',label='Reference') + plt.axhline(pred_new, color='pink', linestyle='--',label='New Data') + plt.ylim([min(min(y),expected_value)*0.98, max(max(y),expected_value)*1.02]) + plt.legend() + plt.close(fig2) + + return fig1, fig2
+ + + +
+[docs] +def partial_dependence(ind, model, data, + ice_color_var=None, + xmin="percentile(0)", xmax="percentile(100)", + npoints=None, feature_names=None, hist=True, model_expected_value=False, + feature_expected_value=False, shap_values=None, + ylabel=None, ice=True, ace_opacity=1, pd_opacity=1, pd_linewidth=2, + ace_linewidth='auto', ax=None, show=True): + """ + Calculates and plots the partial dependence of a feature or a pair of features on the model's output. + + This function is revised from the partial_dependence_plot function in shap package, + in order to color the ICE curves by certain feature for checking interaction between features. + Ref: https://github.com/shap/shap/blob/master/shap/plots/_partial_dependence.py + + Args: + ind (int or tuple): The index or indices of the feature(s) to calculate the partial dependence for. + model: The model used for prediction. + data: The input data used for prediction. + ice_color_var: The index of the feature used for coloring the ICE lines (for 1D partial dependence plot). + xmin (str or tuple or float): The minimum value(s) for the feature(s) range. + xmax (str or tuple or float): The maximum value(s) for the feature(s) range. + npoints (int): The number of points to sample within the feature(s) range. + feature_names (list): The names of the features. + hist (bool): Whether to plot the histogram of the feature(s). + model_expected_value (bool or float): Whether to plot the model's expected value line. + feature_expected_value (bool): Whether to plot the feature's expected value line. + shap_values: The SHAP values used for plotting. + ylabel (str): The label for the y-axis. + ice (bool): Whether to plot the Individual Conditional Expectation (ICE) lines. + ace_opacity (float): The opacity of the ACE lines. + pd_opacity (float): The opacity of the PDP line. + pd_linewidth (float): The linewidth of the PDP line. + ace_linewidth (float or str): The linewidth of the ACE lines. Set to 'auto' for automatic calculation. + ax: The matplotlib axis to plot on. + show (bool): Whether to show the plot. + + Returns: + tuple: A tuple containing the matplotlib figure and axis objects if `show` is False, otherwise None. + """ + + if isinstance(data, Explanation): + features = data.data + shap_values = data + else: + features = data + + # convert from DataFrames if we got any + use_dataframe = False + if isinstance(features, pd.DataFrame): + if feature_names is None: + feature_names = features.columns + features = features.values + use_dataframe = True + + if feature_names is None: + feature_names = ["Feature %d" % i for i in range(features.shape[1])] + + # this is for a 1D partial dependence plot + if type(ind) is not tuple: + ind = convert_name(ind, None, feature_names) + xv = features[:, ind] + xmin, xmax = compute_bounds(xmin, xmax, xv) + npoints = 100 if npoints is None else npoints + xs = np.linspace(xmin, xmax, npoints) + + if ice: + features_tmp = features.copy() + ice_vals = np.zeros((npoints, features.shape[0])) + for i in range(npoints): + features_tmp[:, ind] = xs[i] + if use_dataframe: + ice_vals[i, :] = model(pd.DataFrame(features_tmp, columns=feature_names)) + else: + ice_vals[i, :] = model(features_tmp) + # if linewidth is None: + # linewidth = 1 + # if opacity is None: + # opacity = 0.5 + + features_tmp = features.copy() + vals = np.zeros(npoints) + for i in range(npoints): + features_tmp[:, ind] = xs[i] + if use_dataframe: + vals[i] = model(pd.DataFrame(features_tmp, columns=feature_names)).mean() + else: + vals[i] = model(features_tmp).mean() + + if ax is None: + fig = plt.figure() + ax1 = plt.gca() + else: + fig = plt.gcf() + ax1 = plt.gca() + + # fig, ax1 = plt.subplots(figsize) + ax2 = ax1.twinx() + + # the histogram of the data + if hist: + # n, bins, patches = + ax2.hist(xv, 50, density=False, facecolor='black', alpha=0.1, range=(xmin, xmax)) + + # ice line plot + if ice: + if ace_linewidth == "auto": + ace_linewidth = min(1, 50/ice_vals.shape[1]) # pylint: disable=unsubscriptable-object + # -------------------------- Revised + if ice_color_var is None: + ax1.plot(xs, ice_vals, color=light_blue_rgb, linewidth=ace_linewidth, alpha=ace_opacity) + else: + if not isinstance(ice_color_var, int): + ice_color_var = convert_name(ice_color_var, None, feature_names) + colormap = get_cmap('coolwarm') + color_vals = features[:, ice_color_var] + colorbar_min = color_vals.min() + colorbar_max = color_vals.max() + color_vals = (color_vals - colorbar_min)/(colorbar_max-colorbar_min) + for i in range(ice_vals.shape[0]): + ax1.plot(xs, ice_vals[:, i], color=colormap(color_vals[i]), + linewidth=ace_linewidth, alpha=ace_opacity) + cbar = plt.colorbar(plt.cm.ScalarMappable(cmap=colormap, + norm=plt.Normalize( + vmin=colorbar_min, vmax=colorbar_max)), ax=ax1) + cbar.set_label('Color by ' + feature_names[ice_color_var]) + # -------------------------- + + # the line plot + ax1.plot(xs, vals, color="black", linewidth=pd_linewidth, alpha=pd_opacity) + # Revised: PDP line from blue_rgb to black + + ax2.set_ylim(0, features.shape[0]) # ax2.get_ylim()[0], ax2.get_ylim()[1] * 4) + ax1.set_xlabel(feature_names[ind], fontsize=13) + if ylabel is None: + if not ice: + ylabel = "E[f(x) | " + str(feature_names[ind]) + "]" + else: + ylabel = "f(x) | " + str(feature_names[ind]) + ax1.set_ylabel(ylabel, fontsize=13) + ax1.xaxis.set_ticks_position('bottom') + ax1.yaxis.set_ticks_position('left') + ax1.spines['right'].set_visible(False) + ax1.spines['top'].set_visible(False) + ax1.tick_params(labelsize=11) + + ax2.xaxis.set_ticks_position('bottom') + ax2.yaxis.set_ticks_position('left') + ax2.yaxis.set_ticks([]) + ax2.spines['right'].set_visible(False) + ax2.spines['top'].set_visible(False) + ax2.spines['left'].set_visible(False) + ax2.spines['bottom'].set_visible(False) + + if feature_expected_value is not False: + ax3 = ax2.twiny() + ax3.set_xlim(xmin, xmax) + mval = xv.mean() + ax3.set_xticks([mval]) + ax3.set_xticklabels(["E["+str(feature_names[ind])+"]"]) + ax3.spines['right'].set_visible(False) + ax3.spines['top'].set_visible(False) + ax3.tick_params(length=0, labelsize=11) + ax1.axvline(mval, color="#999999", zorder=-1, linestyle="--", linewidth=1) + + if model_expected_value is not False or shap_values is not None: + if model_expected_value is True: + if use_dataframe: + model_expected_value = model(pd.DataFrame(features, columns=feature_names)).mean() + else: + model_expected_value = model(features).mean() + else: + model_expected_value = shap_values.base_values + ymin, ymax = ax1.get_ylim() + ax4 = ax2.twinx() + ax4.set_ylim(ymin, ymax) + ax4.set_yticks([model_expected_value]) + ax4.set_yticklabels(["E[f(x)]"]) + ax4.spines['right'].set_visible(False) + ax4.spines['top'].set_visible(False) + ax4.tick_params(length=0, labelsize=11) + ax1.axhline(model_expected_value, color="#999999", zorder=-1, linestyle="--", linewidth=1) + + if shap_values is not None: + # vals = shap_values.values[:, ind] + # if shap_value_features is None: + # shap_value_features = features + # assert shap_values.shape == features.shape + # #sample_ind = 18 + # vals = shap_values[:, ind] + # if type(model_expected_value) is bool: + # if use_dataframe: + # model_expected_value = model(pd.DataFrame(features, columns=feature_names)).mean() + # else: + # model_expected_value = model(features).mean() + # if isinstance(shap_value_features, pd.DataFrame): + # shap_value_features = shap_value_features.values + markerline, stemlines, _ = ax1.stem( + shap_values.data[:, ind], shap_values.base_values + shap_values.values[:, ind], + bottom=shap_values.base_values, + markerfmt="o", basefmt=" ", use_line_collection=True + ) + stemlines.set_edgecolors([red_rgb if v > 0 else blue_rgb for v in vals]) + plt.setp(stemlines, 'zorder', -1) + plt.setp(stemlines, 'linewidth', 2) + plt.setp(markerline, 'color', "black") + plt.setp(markerline, 'markersize', 4) + + if show: + plt.show() + else: + return fig, ax1 + + # this is for a 2D partial dependence plot + else: + ind0 = convert_name(ind[0], None, feature_names) + ind1 = convert_name(ind[1], None, feature_names) + xv0 = features[:, ind0] + xv1 = features[:, ind1] + + xmin0 = xmin[0] if type(xmin) is tuple else xmin + xmin1 = xmin[1] if type(xmin) is tuple else xmin + xmax0 = xmax[0] if type(xmax) is tuple else xmax + xmax1 = xmax[1] if type(xmax) is tuple else xmax + + xmin0, xmax0 = compute_bounds(xmin0, xmax0, xv0) + xmin1, xmax1 = compute_bounds(xmin1, xmax1, xv1) + npoints = 20 if npoints is None else npoints + xs0 = np.linspace(xmin0, xmax0, npoints) + xs1 = np.linspace(xmin1, xmax1, npoints) + + features_tmp = features.copy() + x0 = np.zeros((npoints, npoints)) + x1 = np.zeros((npoints, npoints)) + vals = np.zeros((npoints, npoints)) + for i in range(npoints): + for j in range(npoints): + features_tmp[:, ind0] = xs0[i] + features_tmp[:, ind1] = xs1[j] + x0[i, j] = xs0[i] + x1[i, j] = xs1[j] + vals[i, j] = model(features_tmp).mean() + + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + +# x = y = np.arange(-3.0, 3.0, 0.05) +# X, Y = np.meshgrid(x, y) +# zs = np.array(fun(np.ravel(X), np.ravel(Y))) +# Z = zs.reshape(X.shape) + + ax.plot_surface(x0, x1, vals, cmap=red_blue_transparent) + + ax.set_xlabel(feature_names[ind0], fontsize=13) + ax.set_ylabel(feature_names[ind1], fontsize=13) + ax.set_zlabel("E[f(x) | " + str(feature_names[ind0]) + ", " + str(feature_names[ind1]) + "]", fontsize=13) + + if show: + plt.show() + else: + return fig, ax
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/surrogates/base.html b/_modules/obsidian/surrogates/base.html new file mode 100644 index 0000000..dc03dba --- /dev/null +++ b/_modules/obsidian/surrogates/base.html @@ -0,0 +1,645 @@ + + + + + + + + + + obsidian.surrogates.base — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.surrogates.base

+"""Surrogate model class definition"""
+
+from obsidian.config import TORCH_DTYPE
+
+from abc import ABC, abstractmethod
+
+import numpy as np
+import pandas as pd
+import torch
+import random
+
+
+
+[docs] +class SurrogateModel(ABC): + """ + The model used for conducting optimization. Consumes data and produces a regressed representation + of that system. Model can then be used to make predictions or evaluate uncertainty. + + Attributes: + is_fit (bool): Flag for fitting (e.g. to prevent predicting from an unfit model). + model_type (str): The type of the model. + train_X (pd.DataFrame): The input data for the training data. + train_Y (pd.Series): The target data for the training data. + cat_dims (list): The categorical dimensions of the data. + task_feature (str): The task feature of the data. + X_order (list): The order of the columns in the input data. + y_name (str): The name of the target column. + seed (int): Randomization seed for stochastic surrogate models. + verbose (bool): Flag for monitoring and debugging optimization + """ +
+[docs] + def __init__(self, + model_type: str = 'GP', + seed: int | None = None, + verbose: bool = False): + + # Set a flag for fitting (e.g. to prevent predicting from an unfit model) + self.is_fit = False + + # Set up initial model state + self.model_type = model_type + self.train_X = None + self.train_Y = None + self.cat_dims = None + self.task_feature = None + self.X_order = None + self.y_name = None + + # Handle randomization seed, considering all 3 sources (torch, random, numpy) + self.seed = seed + if self.seed is not None: + torch.manual_seed(self.seed) + if not torch.cuda.is_available(): + torch.use_deterministic_algorithms(True) + np.random.seed(self.seed) + random.seed(self.seed) + + # Verbose output + self.verbose = verbose + + return
+ + + def _validate_data(self, + X: pd.DataFrame, + y: pd.Series | None = None): + """ + Validate the data properties, e.g. column order + + Args: + X (pd.DataFrame): Input parameters for the training data. + y (pd.Series): Training data responses. + + Raises: + ValueError: If the data properties do not match the training data. + """ + + if X.columns.tolist() != self.X_order: + raise ValueError('X columns do not match training data') + if y is not None: + if y.name != self.y_name: + raise ValueError('y column does not match training data') + return + + def _prepare(self, + X: pd.DataFrame, + y: pd.Series | None = None) -> tuple: + """ + Converts X, Y into appropriate Tensor dtypes and shapes for torch_model + + Args: + X (pd.DataFrame): The input data as a pandas DataFrame. + Y (pd.Series): The target data as a pandas Series. + + Returns: + tuple: A tuple containing the converted input data (X_torch) and target data (Y_torch) as torch Tensors. + """ + self._validate_data(X, y) + X_torch = torch.tensor(X.to_numpy(), dtype=TORCH_DTYPE).to(self.device) + if y is not None: + y_torch = torch.tensor(y.to_numpy(), dtype=TORCH_DTYPE).to(self.device).unsqueeze(-1) + return (X_torch, y_torch) + else: + return X_torch + +
+[docs] + @abstractmethod + def fit(self, + X: pd.DataFrame, + y: pd.Series): + """Fit the surrogate model to data""" + pass # pragma: no cover
+ + +
+[docs] + @abstractmethod + def predict(self, + X: pd.DataFrame): + """Predict outputs based on candidates X""" + pass # pragma: no cover
+ + +
+[docs] + @abstractmethod + def score(self, + X: pd.DataFrame, + y: pd.Series): + """Score the model based on the given test data""" + pass # pragma: no cover
+ + +
+[docs] + @abstractmethod + def save_state(self): + """Save the model to a state dictionary""" + pass # pragma: no cover
+ + +
+[docs] + @classmethod + @abstractmethod + def load_state(cls, + obj_dict: dict): + """Load the model from a state dictionary""" + pass # pragma: no cover
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/surrogates/botorch.html b/_modules/obsidian/surrogates/botorch.html new file mode 100644 index 0000000..90ca6e9 --- /dev/null +++ b/_modules/obsidian/surrogates/botorch.html @@ -0,0 +1,822 @@ + + + + + + + + + + obsidian.surrogates.botorch — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.surrogates.botorch

+"""Surrogate models built using BoTorch API and torch_model objects"""
+
+from .base import SurrogateModel
+from .config import model_class_dict
+
+from obsidian.utils import tensordict_to_dict, dict_to_tensordict
+from obsidian.exceptions import SurrogateFitError
+from obsidian.config import TORCH_DTYPE
+
+from botorch.fit import fit_gpytorch_mll
+from botorch.optim.fit import fit_gpytorch_mll_torch, fit_gpytorch_mll_scipy
+from botorch.models.gpytorch import GPyTorchModel
+from gpytorch.mlls import ExactMarginalLogLikelihood
+
+import torch
+import torch.nn as nn
+import torch.optim as optim
+import numpy as np
+import pandas as pd
+import warnings
+
+
+
+[docs] +class SurrogateBoTorch(SurrogateModel): + """ + BoTorch GP model, subclass of the obsidian SurrogateModel + + Attributes: + model_type (str): The type of model to be used. + + Defaults to ``'GP'``. Options are as follows: + + - ``'GP'``: Gaussian Process with default settings (Matern Kernel, Gamma covariance priors) + - ``'MixedGP'``: GP with mixed parameter types (continuous, categorical). Will be re-selected + by default if 'GP' is selected and input space is mixed. + - ``'DKL'``: GP with a NN feature-extractor (deep kernel learning) + - ``'GPflat'``: GP without priors. May result in optimization instability, but removes bias + for special situations. + - ``'GPprior'``: GP with custom priors on the mean, likelihood, and covariance + - ``'MTGP'``: Multi-task GP for multi-output optimization. Will be re-selected by default + if 'GP' is selected and the input space contains Task parameters. + - ``'DNN'``: Dropout neural network. Uses MC sampling to mask neurons during training and + to estimate uncertainty. + + device (str): The device on which the model is run. + hps (dict): Optional surrogate function hyperparameters. + mll (ExactMarginalLogLikelihood): The marginal log likelihood of the model. + torch_model (torch.nn.Module): The torch model for the surrogate. + loss (float): The loss of the model. + score (float): The R2 score of the model. + """ +
+[docs] + def __init__(self, + model_type: str = 'GP', + seed: int | None = None, + verbose: int | None = False, + hps: dict = {}): + + super().__init__(model_type=model_type, seed=seed, verbose=verbose) + + if torch.cuda.is_available(): + self.device = 'cuda' + else: + self.device = 'cpu' + + # Optional surrogate function hyperparameters + self.hps = hps + + return
+ + +
+[docs] + def init_model(self, + X: pd.DataFrame, + y: pd.Series, + cat_dims: list[int], + task_feature: int): + """ + Instantiates the torch model for the surrogate. + Cannot be called during __init__ normally as X,y are required + and may not be available until fit methods are called + + Args: + X (pd.DataFrame): Input parameters for the training data. + y (pd.Series): Training data responses. + cat_dims (list): A list of indices for categorical dimensions in the input data. + + Returns: + None. Updates surrogate attributes, including self.torch_model. + + Raises: + TypeError: If cat_dims is not a list of integers. + + """ + + # Once the model is created, steward the order of data columns + self.X_order = X.columns.tolist() + self.y_name = y.name + + X_p, y_p = self._prepare(X, y) + + if not isinstance(cat_dims, list): + raise TypeError('cat_dims must be a list') + if not all([isinstance(c, int) for c in cat_dims]): + raise TypeError('cat_dims must be a list of integers') + + if issubclass(model_class_dict[self.model_type], GPyTorchModel): + if self.model_type == 'GP' and cat_dims: # If cat_dims is not an empty list, returns True + self.torch_model = model_class_dict['MixedGP'](train_X=X_p, train_Y=y_p, cat_dims=cat_dims).to(self.device) + else: + if self.model_type == 'MTGP': + self.torch_model = model_class_dict[self.model_type]( + train_X=X_p, train_Y=y_p, task_feature=task_feature, **self.hps).to(self.device) + else: + # Note: Doesn't matter if input empty dictionary as self.hps for model without those additional args + self.torch_model = model_class_dict[self.model_type](train_X=X_p, train_Y=y_p, **self.hps).to(self.device) + else: + self.torch_model = model_class_dict[self.model_type](train_X=X_p, train_Y=y_p, **self.hps).to(self.device).to(TORCH_DTYPE) + + return
+ + +
+[docs] + def fit(self, + X: pd.DataFrame, + y: pd.Series, + cat_dims=None, + task_feature=None): + """ + Fits the surrogate model to data + + Args: + X (pd.DataFrame): Input parameters for the training data + y (pd.Series): Training data responses + cat_dims (list, optional): A list of indices for categorical dimensions in the input data. Default is ``None``. + + Returns: + None. Updates the surrogate model attributes, including regressed parameters. + """ + + # Instantiate self.torch_model + self.init_model(X, y, cat_dims, task_feature) + X_p, y_p = self._prepare(X, y) + + # Save the raw data inputs into the model metadata + self.train_X = X + self.train_Y = y + self.cat_dims = cat_dims + self.task_feature = task_feature + + # Train + if isinstance(self.torch_model, GPyTorchModel): + self.loss_fcn = ExactMarginalLogLikelihood(self.torch_model.likelihood, self.torch_model) + if self.model_type == 'DKL': + optimizer = fit_gpytorch_mll_torch + else: + optimizer = fit_gpytorch_mll_scipy + + try: + fit_gpytorch_mll(self.loss_fcn, optimizer=optimizer) + except Exception: + try: + fit_gpytorch_mll(self.loss_fcn, optimizer=fit_gpytorch_mll_torch) + except Exception: + raise SurrogateFitError('BoTorch model failed to fit') + else: + self.loss_fcn = nn.MSELoss() + self.optimizer = optim.Adam(self.torch_model.parameters(), lr=1e-2) + + self.torch_model.train() + for epoch in range(200): + self.optimizer.zero_grad() + output = self.torch_model(X_p) + loss = self.loss_fcn(output, y_p) + loss.backward() + self.optimizer.step() + + self.torch_model.eval() + + self.is_fit = True + + # Quick evaluation + self.score(X, y) + + return
+ + +
+[docs] + def score(self, + X: pd.DataFrame, + y: pd.Series) -> tuple: + """ + Computes simple model statistics on a dataset. + + Args: + X (pd.DataFrame): Input parameters for the evaluation data. + y (pd.DataFrame): Evaluation data responses. + + Returns: + tuple: A tuple containing the loss and R2 score of the evaluation. + """ + + X_p, y_p = self._prepare(X, y) + + if self.model_type == 'DKL': + self.torch_model(X_p) + + # Quick evaluation + # Suppress an error that will be generated when evaluating R2_training + warnings.filterwarnings('ignore', message=r'[.\n]*The input matches the stored training data*') + mu_pred, _ = self.predict(X) + corr_matrix = np.corrcoef(mu_pred.detach().cpu().flatten(), + y_p.detach().cpu().flatten()) + + # Calculate a final loss and R2 train score + loss = self.loss = self.loss_fcn(self.torch_model(X_p), y_p).sum().detach().cpu().data.numpy().tolist() + score = self.score = corr_matrix[0][1]**2 + + return loss, score
+ + +
+[docs] + def predict(self, + X: pd.DataFrame, + q: float | None = None): + """ + Computes model predictions over new experimental space. + + Args: + X (pd.DataFrame): Input parameters for the prediction space. + q (float, op) + + Returns: + tuple: A tuple containing: + - mu (ndarray): Mean responses for each experiment in the prediction space. + - q_pred | sd (ndarray): Quantile or standard deviation of the predicted response for each experiment. + """ + + X_p = self._prepare(X) + + pred_posterior = self.torch_model.posterior(X_p) + mu = pred_posterior.mean.detach().cpu().squeeze(-1) + if q is not None: + if (q < 0) or (q > 1): + raise ValueError('Quantile must be between 0 and 1') + q_pred = pred_posterior.quantile(torch.tensor(q)).detach().cpu().squeeze(-1) + return mu, q_pred + else: + sd = pred_posterior.variance.detach().cpu().squeeze(-1)**0.5 + return mu, sd
+ + +
+[docs] + def save_state(self) -> dict: + """ + Saves the state of the SurrogateBoTorch model. + + Returns: + dict: A dictionary containing the state of the SurrogateBoTorch model. + """ + obj_dict = {'model_attrs': {}} + + model_attrs = ['model_type', 'seed', 'hps', + 'is_fit', + 'cat_dims', 'task_feature', + 'X_order', 'y_name'] + for attr in model_attrs: + obj_dict['model_attrs'][attr] = getattr(self, attr) + + obj_dict['train_X'] = self.train_X.to_dict() + obj_dict['train_Y'] = self.train_Y.to_dict() + + # Use torch's built in state_dict function + obj_dict['torch_params'] = tensordict_to_dict(self.torch_model.state_dict()) + + return obj_dict
+ + +
+[docs] + @classmethod + def load_state(cls, + obj_dict: dict): + """ + Generates a BoTorch surrogate model from a state dictionary (saved after fitting). + Note that the training data is necessary to instantiate a matching model, + but no fitting occurs here. + + Args: + obj_dict (OrderedDict): The state dictionary of a previously fit BoTorch model. + + Returns: + None. Updates the parameters of the model. + """ + + new_model = cls(model_type=obj_dict['model_attrs']['model_type'], + seed=obj_dict['model_attrs']['seed'], verbose=False, + hps=obj_dict['model_attrs']['hps']) + + # Directly unpack all of the entries in model_attrs + for k, v in obj_dict['model_attrs'].items(): + setattr(new_model, k, v) + + # Load data objects + new_model.train_X = pd.DataFrame(obj_dict['train_X'], columns=new_model.X_order) + new_model.train_Y = pd.Series(obj_dict['train_Y'], name=new_model.y_name) + + # Re-initialize torch model + new_model.init_model(new_model.train_X, new_model.train_Y, new_model.cat_dims, new_model.task_feature) + + # Load saved parameters into the torch model + new_model.torch_model.load_state_dict(dict_to_tensordict(obj_dict['torch_params'])) + + # Rebuild MLL which would have been done during fitting + new_model.torch_model.training = False + if isinstance(new_model.torch_model, GPyTorchModel): + new_model.loss_fcn = ExactMarginalLogLikelihood(new_model.torch_model.likelihood, new_model.torch_model) + else: + new_model.loss_fcn = nn.MSELoss() + new_model.score(new_model.train_X, new_model.train_Y) + + return new_model
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/surrogates/custom_GP.html b/_modules/obsidian/surrogates/custom_GP.html new file mode 100644 index 0000000..7218f3f --- /dev/null +++ b/_modules/obsidian/surrogates/custom_GP.html @@ -0,0 +1,643 @@ + + + + + + + + + + obsidian.surrogates.custom_GP — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.surrogates.custom_GP

+"""Custom implementations of Gaussian Process models using BoTorch API"""
+
+from obsidian.config import TORCH_DTYPE
+
+from botorch.models.gpytorch import GPyTorchModel
+from gpytorch.distributions import MultivariateNormal
+from gpytorch.likelihoods import GaussianLikelihood
+
+from gpytorch.means import ConstantMean
+from gpytorch.models import ExactGP
+
+from gpytorch.kernels import ScaleKernel, MaternKernel
+from gpytorch.priors import GammaPrior
+from gpytorch.constraints import GreaterThan
+
+import torch
+from torch import nn
+import gpytorch
+
+
+
+[docs] +class PriorGP(ExactGP, GPyTorchModel): + """ + Class which builds a GP with custom prior distributions; + by default set to the values of BoTorch SingleTaskGP + """ + _num_outputs = 1 + +
+[docs] + def __init__(self, train_X, train_Y): + + # Prior on the noise (custom likelihood) + noise_prior = GammaPrior(1.1, 0.05) + noise_prior_mode = (noise_prior.concentration - 1) / noise_prior.rate + likelihood = GaussianLikelihood( + noise_prior=noise_prior, + noise_constraint=GreaterThan( + 1e-4, + transform=None, + initial_value=noise_prior_mode, + ), + ) + + super().__init__(train_X, train_Y.squeeze(-1), likelihood) + + n_dims = train_X.shape[-1] + + self.mean_module = ConstantMean() + self.covar_module = ScaleKernel( + base_kernel=MaternKernel(nu=2.5, ard_num_dims=n_dims, + lengthscale_prior=GammaPrior(3.0, 6.0)), + outputscale_prior=GammaPrior(2.0, 0.15), + )
+ + +
+[docs] + def forward(self, x): + """Evaluate the forward pass of the model on inputs X""" + mean_x = self.mean_module(x) + covar_x = self.covar_module(x) + return MultivariateNormal(mean_x, covar_x)
+
+ + + +
+[docs] +class FlatGP(ExactGP, GPyTorchModel): + """ + GP Surrogate with non-informative or no prior distributions + """ + _num_outputs = 1 + +
+[docs] + def __init__(self, train_X, train_Y, nu=2.5): + + super().__init__(train_X, train_Y.squeeze(-1), GaussianLikelihood()) + + n_dims = train_X.shape[-1] + + self.mean_module = ConstantMean() + self.covar_module = ScaleKernel( + base_kernel=MaternKernel(nu=nu, ard_num_dims=n_dims), + )
+ + +
+[docs] + def forward(self, x): + """Evaluate the forward pass of the model on inputs X""" + mean_x = self.mean_module(x) + covar_x = self.covar_module(x) + return MultivariateNormal(mean_x, covar_x)
+
+ + + +
+[docs] +class DKLGP(ExactGP, GPyTorchModel): + """ + GP surrogate with a FF NN feature extractor + """ + _num_outputs = 1 + +
+[docs] + def __init__(self, train_X, train_Y): + + super().__init__(train_X, train_Y.squeeze(-1), GaussianLikelihood()) + + n_dims = train_X.shape[-1] + + self.mean_module = ConstantMean() + self.covar_module = ScaleKernel( + base_kernel=MaternKernel(nu=2.5, ard_num_dims=n_dims), + ) + + # Set up the NN feature extractor + torch.set_default_dtype(TORCH_DTYPE) + self.feature_extractor = nn.Sequential( + # Hiden layer 1 + nn.Linear(n_dims, n_dims), + nn.PReLU(), + # Hidden layer 2 + nn.Linear(n_dims, n_dims), + nn.PReLU(), + # #Output layer + nn.Linear(n_dims, n_dims) + ) + + # Use a scaler to make sure the GP only sees well-conditioned values + self.scale_to_bounds = gpytorch.utils.grid.ScaleToBounds(0, 1)
+ + +
+[docs] + def forward(self, x): + """Evaluate the forward pass of the model on inputs X""" + # Pass the data through the feature extractor and scaler + projected_x = self.feature_extractor(x) + projected_x = self.scale_to_bounds(projected_x) + + mean_x = self.mean_module(projected_x) + covar_x = self.covar_module(projected_x) + return MultivariateNormal(mean_x, covar_x)
+
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_modules/obsidian/surrogates/custom_torch.html b/_modules/obsidian/surrogates/custom_torch.html new file mode 100644 index 0000000..bfe53d4 --- /dev/null +++ b/_modules/obsidian/surrogates/custom_torch.html @@ -0,0 +1,581 @@ + + + + + + + + + + obsidian.surrogates.custom_torch — obsidian documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +

Source code for obsidian.surrogates.custom_torch

+"""Custom implementations of PyTorch surrogate models using BoTorch API"""
+
+from botorch.models.model import Model
+from botorch.posteriors.ensemble import Posterior, EnsemblePosterior
+
+import torch.nn as nn
+from torch import Tensor
+
+
+
+[docs] +class DNN(Model): +
+[docs] + def __init__(self, + train_X: Tensor, + train_Y: Tensor, + p_dropout: float = 0.2, + h_width: int = 16, + h_layers: int = 2, + num_outputs: int = 1): + + super().__init__() + + if h_layers < 1: + raise ValueError("h_layers must be at least 1") + if p_dropout < 0 or p_dropout > 1: + raise ValueError("p_dropout must be in [0, 1]") + + self.input_layer = nn.Sequential( + nn.Linear(train_X.shape[-1], h_width), + nn.PReLU(), + nn.Dropout(p=p_dropout) + ) + + self.middle_layers = nn.Sequential( + *[nn.Sequential( + nn.Linear(h_width, h_width), + nn.PReLU(), + nn.Dropout(p=p_dropout) + ) for _ in range(h_layers)] + ) + + self.outer_layer = nn.Linear(h_width, num_outputs) + self._num_outputs = num_outputs
+ + +
+[docs] + def forward(self, + x: Tensor) -> Tensor: + x = self.input_layer(x) + x = self.middle_layers(x) + x = self.outer_layer(x) + """Evaluate the forward pass of the model on inputs X""" + return x
+ + +
+[docs] + def posterior(self, + X: Tensor, + n_sample: int = 1024, + output_indices: list[int] = None, + observation_noise: bool | Tensor = False) -> Posterior: + """Calculates the posterior distribution of the model at X""" + if not output_indices: + output_indices = list(range(self._num_outputs)) + elif not all(0 <= i < self._num_outputs for i in output_indices): + raise ValueError("Invalid output index") + + if X.ndim == 2: + X_sample = X.unsqueeze(0).repeat_interleave(n_sample, 0) + else: # Account for possible batch dimension + X_sample = X.unsqueeze(1).repeat_interleave(n_sample, 1) + + self.train() + y_out = self.forward(X_sample) + self.eval() + + post = EnsemblePosterior(y_out[..., output_indices]) + + return post
+ + + @property + def num_outputs(self) -> int: + """Number of outputs of the model""" + return self._num_outputs
+ +
+ +
+ + + + + +
+ +
+
+
+ +
+ + + + +
+ + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.acquisition.botorch.rst.txt b/_sources/build/apidocs/obsidian.acquisition.botorch.rst.txt new file mode 100644 index 0000000..1e8ca69 --- /dev/null +++ b/_sources/build/apidocs/obsidian.acquisition.botorch.rst.txt @@ -0,0 +1,9 @@ +botorch +======= + +.. automodule:: obsidian.acquisition.botorch + + + + + diff --git a/_sources/build/apidocs/obsidian.acquisition.custom.rst.txt b/_sources/build/apidocs/obsidian.acquisition.custom.rst.txt new file mode 100644 index 0000000..159eed4 --- /dev/null +++ b/_sources/build/apidocs/obsidian.acquisition.custom.rst.txt @@ -0,0 +1,17 @@ +custom +====== + +.. automodule:: obsidian.acquisition.custom + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + + ~qMean + ~qSpaceFill + + + + diff --git a/_sources/build/apidocs/obsidian.acquisition.rst.txt b/_sources/build/apidocs/obsidian.acquisition.rst.txt new file mode 100644 index 0000000..9611968 --- /dev/null +++ b/_sources/build/apidocs/obsidian.acquisition.rst.txt @@ -0,0 +1,22 @@ +acquisition +=========== + +.. automodule:: obsidian.acquisition + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module_shallow.rst + :recursive: + + + botorch + + + custom + diff --git a/_sources/build/apidocs/obsidian.campaign.analysis.calc_ofat_ranges.rst.txt b/_sources/build/apidocs/obsidian.campaign.analysis.calc_ofat_ranges.rst.txt new file mode 100644 index 0000000..319f086 --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.analysis.calc_ofat_ranges.rst.txt @@ -0,0 +1,6 @@ +calc\_ofat\_ranges +================== + +.. currentmodule:: obsidian.campaign.analysis + +.. autofunction:: calc_ofat_ranges \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.campaign.analysis.plot_interactions.rst.txt b/_sources/build/apidocs/obsidian.campaign.analysis.plot_interactions.rst.txt new file mode 100644 index 0000000..cec2494 --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.analysis.plot_interactions.rst.txt @@ -0,0 +1,6 @@ +plot\_interactions +================== + +.. currentmodule:: obsidian.campaign.analysis + +.. autofunction:: plot_interactions \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.campaign.analysis.plot_ofat_ranges.rst.txt b/_sources/build/apidocs/obsidian.campaign.analysis.plot_ofat_ranges.rst.txt new file mode 100644 index 0000000..68591ed --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.analysis.plot_ofat_ranges.rst.txt @@ -0,0 +1,6 @@ +plot\_ofat\_ranges +================== + +.. currentmodule:: obsidian.campaign.analysis + +.. autofunction:: plot_ofat_ranges \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.campaign.analysis.rst.txt b/_sources/build/apidocs/obsidian.campaign.analysis.rst.txt new file mode 100644 index 0000000..65e2fe0 --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.analysis.rst.txt @@ -0,0 +1,21 @@ +analysis +======== + +.. automodule:: obsidian.campaign.analysis + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + :toctree: + + ~calc_ofat_ranges + ~plot_interactions + ~plot_ofat_ranges + ~sensitivity + + + + + diff --git a/_sources/build/apidocs/obsidian.campaign.analysis.sensitivity.rst.txt b/_sources/build/apidocs/obsidian.campaign.analysis.sensitivity.rst.txt new file mode 100644 index 0000000..dfd1a1a --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.analysis.sensitivity.rst.txt @@ -0,0 +1,6 @@ +sensitivity +=========== + +.. currentmodule:: obsidian.campaign.analysis + +.. autofunction:: sensitivity \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.campaign.campaign.Campaign.rst.txt b/_sources/build/apidocs/obsidian.campaign.campaign.Campaign.rst.txt new file mode 100644 index 0000000..349b9cb --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.campaign.Campaign.rst.txt @@ -0,0 +1,48 @@ +Campaign +======== + +.. currentmodule:: obsidian.campaign.campaign + +.. autoclass:: Campaign + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Campaign.__init__ + ~Campaign.add_data + ~Campaign.clear_data + ~Campaign.fit + ~Campaign.initialize + ~Campaign.load_state + ~Campaign.save_state + ~Campaign.set_designer + ~Campaign.set_objective + ~Campaign.set_optimizer + ~Campaign.set_target + ~Campaign.suggest + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~Campaign.X + ~Campaign.designer + ~Campaign.f + ~Campaign.m_exp + ~Campaign.o + ~Campaign.objective + ~Campaign.optimizer + ~Campaign.response_max + ~Campaign.target + ~Campaign.y + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.campaign.campaign.rst.txt b/_sources/build/apidocs/obsidian.campaign.campaign.rst.txt new file mode 100644 index 0000000..a1d2a81 --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.campaign.rst.txt @@ -0,0 +1,18 @@ +campaign +======== + +.. automodule:: obsidian.campaign.campaign + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~Campaign + + + + + diff --git a/_sources/build/apidocs/obsidian.campaign.explainer.Explainer.rst.txt b/_sources/build/apidocs/obsidian.campaign.explainer.Explainer.rst.txt new file mode 100644 index 0000000..025a2e9 --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.explainer.Explainer.rst.txt @@ -0,0 +1,35 @@ +Explainer +========= + +.. currentmodule:: obsidian.campaign.explainer + +.. autoclass:: Explainer + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Explainer.__init__ + ~Explainer.cal_sensitivity + ~Explainer.set_optimizer + ~Explainer.shap_explain + ~Explainer.shap_pdp_ice + ~Explainer.shap_single_point + ~Explainer.shap_summary + ~Explainer.shap_summary_bar + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~Explainer.optimizer + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.campaign.explainer.rst.txt b/_sources/build/apidocs/obsidian.campaign.explainer.rst.txt new file mode 100644 index 0000000..87838c1 --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.explainer.rst.txt @@ -0,0 +1,18 @@ +explainer +========= + +.. automodule:: obsidian.campaign.explainer + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~Explainer + + + + + diff --git a/_sources/build/apidocs/obsidian.campaign.rst.txt b/_sources/build/apidocs/obsidian.campaign.rst.txt new file mode 100644 index 0000000..54659f0 --- /dev/null +++ b/_sources/build/apidocs/obsidian.campaign.rst.txt @@ -0,0 +1,24 @@ +campaign +======== + +.. automodule:: obsidian.campaign + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module.rst + :recursive: + + + analysis + + campaign + + explainer + + diff --git a/_sources/build/apidocs/obsidian.constraints.input.rst.txt b/_sources/build/apidocs/obsidian.constraints.input.rst.txt new file mode 100644 index 0000000..9f8c5c4 --- /dev/null +++ b/_sources/build/apidocs/obsidian.constraints.input.rst.txt @@ -0,0 +1,17 @@ +input +===== + +.. automodule:: obsidian.constraints.input + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + + ~InConstraint_ConstantDim + ~InConstraint_Generic + + + + diff --git a/_sources/build/apidocs/obsidian.constraints.output.rst.txt b/_sources/build/apidocs/obsidian.constraints.output.rst.txt new file mode 100644 index 0000000..9bc96ca --- /dev/null +++ b/_sources/build/apidocs/obsidian.constraints.output.rst.txt @@ -0,0 +1,17 @@ +output +====== + +.. automodule:: obsidian.constraints.output + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + + ~OutConstraint_Blank + ~OutConstraint_L1 + + + + diff --git a/_sources/build/apidocs/obsidian.constraints.rst.txt b/_sources/build/apidocs/obsidian.constraints.rst.txt new file mode 100644 index 0000000..3b228c4 --- /dev/null +++ b/_sources/build/apidocs/obsidian.constraints.rst.txt @@ -0,0 +1,21 @@ +obsidian.constraints +==================== + +.. automodule:: obsidian.constraints + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module_shallow.rst + :recursive: + + + input + + output + diff --git a/_sources/build/apidocs/obsidian.exceptions.rst.txt b/_sources/build/apidocs/obsidian.exceptions.rst.txt new file mode 100644 index 0000000..800e6d3 --- /dev/null +++ b/_sources/build/apidocs/obsidian.exceptions.rst.txt @@ -0,0 +1,20 @@ +exceptions +========== + +.. automodule:: obsidian.exceptions + + + .. rubric:: Exceptions + + .. autosummary:: + :template: base.rst + + ~DataWarning + ~IncompatibleObjectiveError + ~SurrogateFitError + ~UnfitError + ~UnsupportedError + + + + diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.ackley.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.ackley.rst.txt new file mode 100644 index 0000000..3d18090 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.ackley.rst.txt @@ -0,0 +1,6 @@ +ackley +====== + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: ackley \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.branin_currin.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.branin_currin.rst.txt new file mode 100644 index 0000000..67b4ec5 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.branin_currin.rst.txt @@ -0,0 +1,6 @@ +branin\_currin +============== + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: branin_currin \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.cornered_parab.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.cornered_parab.rst.txt new file mode 100644 index 0000000..e7e3e5e --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.cornered_parab.rst.txt @@ -0,0 +1,6 @@ +cornered\_parab +=============== + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: cornered_parab \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.paraboloid.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.paraboloid.rst.txt new file mode 100644 index 0000000..c69c72a --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.paraboloid.rst.txt @@ -0,0 +1,6 @@ +paraboloid +========== + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: paraboloid \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.perm.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.perm.rst.txt new file mode 100644 index 0000000..02ebe16 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.perm.rst.txt @@ -0,0 +1,6 @@ +perm +==== + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: perm \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.rosenbrock.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.rosenbrock.rst.txt new file mode 100644 index 0000000..33276bc --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.rosenbrock.rst.txt @@ -0,0 +1,6 @@ +rosenbrock +========== + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: rosenbrock \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.rst.txt new file mode 100644 index 0000000..359166c --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.rst.txt @@ -0,0 +1,27 @@ +geometric +========= + +.. automodule:: obsidian.experiment.benchmark.geometric + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + :toctree: + + ~ackley + ~branin_currin + ~cornered_parab + ~paraboloid + ~perm + ~rosenbrock + ~shifted_parab + ~sixhump_camel + ~threehump_camel + ~two_leaves + + + + + diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.shifted_parab.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.shifted_parab.rst.txt new file mode 100644 index 0000000..6fb5761 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.shifted_parab.rst.txt @@ -0,0 +1,6 @@ +shifted\_parab +============== + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: shifted_parab \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.sixhump_camel.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.sixhump_camel.rst.txt new file mode 100644 index 0000000..84555e9 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.sixhump_camel.rst.txt @@ -0,0 +1,6 @@ +sixhump\_camel +============== + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: sixhump_camel \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.threehump_camel.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.threehump_camel.rst.txt new file mode 100644 index 0000000..5ae3a03 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.threehump_camel.rst.txt @@ -0,0 +1,6 @@ +threehump\_camel +================ + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: threehump_camel \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.two_leaves.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.two_leaves.rst.txt new file mode 100644 index 0000000..fd21cac --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.geometric.two_leaves.rst.txt @@ -0,0 +1,6 @@ +two\_leaves +=========== + +.. currentmodule:: obsidian.experiment.benchmark.geometric + +.. autofunction:: two_leaves \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.Km_func.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.Km_func.rst.txt new file mode 100644 index 0000000..d5af2b2 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.Km_func.rst.txt @@ -0,0 +1,6 @@ +Km\_func +======== + +.. currentmodule:: obsidian.experiment.benchmark.optithon + +.. autofunction:: Km_func \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.OT_simulator.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.OT_simulator.rst.txt new file mode 100644 index 0000000..fa2901e --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.OT_simulator.rst.txt @@ -0,0 +1,6 @@ +OT\_simulator +============= + +.. currentmodule:: obsidian.experiment.benchmark.optithon + +.. autofunction:: OT_simulator \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.Vm_func.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.Vm_func.rst.txt new file mode 100644 index 0000000..9fdc9a3 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.Vm_func.rst.txt @@ -0,0 +1,6 @@ +Vm\_func +======== + +.. currentmodule:: obsidian.experiment.benchmark.optithon + +.. autofunction:: Vm_func \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.kI_func.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.kI_func.rst.txt new file mode 100644 index 0000000..60c7f57 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.kI_func.rst.txt @@ -0,0 +1,6 @@ +kI\_func +======== + +.. currentmodule:: obsidian.experiment.benchmark.optithon + +.. autofunction:: kI_func \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.response_1.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.response_1.rst.txt new file mode 100644 index 0000000..305838b --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.response_1.rst.txt @@ -0,0 +1,6 @@ +response\_1 +=========== + +.. currentmodule:: obsidian.experiment.benchmark.optithon + +.. autofunction:: response_1 \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.response_2.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.response_2.rst.txt new file mode 100644 index 0000000..d2dcf36 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.response_2.rst.txt @@ -0,0 +1,6 @@ +response\_2 +=========== + +.. currentmodule:: obsidian.experiment.benchmark.optithon + +.. autofunction:: response_2 \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.rst.txt new file mode 100644 index 0000000..d8039ca --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.optithon.rst.txt @@ -0,0 +1,23 @@ +optithon +======== + +.. automodule:: obsidian.experiment.benchmark.optithon + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + :toctree: + + ~Km_func + ~OT_simulator + ~Vm_func + ~kI_func + ~response_1 + ~response_2 + + + + + diff --git a/_sources/build/apidocs/obsidian.experiment.benchmark.rst.txt b/_sources/build/apidocs/obsidian.experiment.benchmark.rst.txt new file mode 100644 index 0000000..ed43302 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.benchmark.rst.txt @@ -0,0 +1,22 @@ +benchmark +========= + +.. automodule:: obsidian.experiment.benchmark + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module.rst + :recursive: + + + geometric + + optithon + + diff --git a/_sources/build/apidocs/obsidian.experiment.design.ExpDesigner.rst.txt b/_sources/build/apidocs/obsidian.experiment.design.ExpDesigner.rst.txt new file mode 100644 index 0000000..2b62e94 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.design.ExpDesigner.rst.txt @@ -0,0 +1,23 @@ +ExpDesigner +=========== + +.. currentmodule:: obsidian.experiment.design + +.. autoclass:: ExpDesigner + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~ExpDesigner.__init__ + ~ExpDesigner.initialize + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.design.rst.txt b/_sources/build/apidocs/obsidian.experiment.design.rst.txt new file mode 100644 index 0000000..7ffc2c8 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.design.rst.txt @@ -0,0 +1,18 @@ +design +====== + +.. automodule:: obsidian.experiment.design + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~ExpDesigner + + + + + diff --git a/_sources/build/apidocs/obsidian.experiment.rst.txt b/_sources/build/apidocs/obsidian.experiment.rst.txt new file mode 100644 index 0000000..9fb1a84 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.rst.txt @@ -0,0 +1,26 @@ +experiment +========== + +.. automodule:: obsidian.experiment + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module.rst + :recursive: + + + benchmark + + design + + simulator + + utils + + diff --git a/_sources/build/apidocs/obsidian.experiment.simulator.Simulator.rst.txt b/_sources/build/apidocs/obsidian.experiment.simulator.Simulator.rst.txt new file mode 100644 index 0000000..558ac24 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.simulator.Simulator.rst.txt @@ -0,0 +1,23 @@ +Simulator +========= + +.. currentmodule:: obsidian.experiment.simulator + +.. autoclass:: Simulator + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Simulator.__init__ + ~Simulator.simulate + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.simulator.rst.txt b/_sources/build/apidocs/obsidian.experiment.simulator.rst.txt new file mode 100644 index 0000000..2ba514e --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.simulator.rst.txt @@ -0,0 +1,18 @@ +simulator +========= + +.. automodule:: obsidian.experiment.simulator + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~Simulator + + + + + diff --git a/_sources/build/apidocs/obsidian.experiment.utils.factorial_DOE.rst.txt b/_sources/build/apidocs/obsidian.experiment.utils.factorial_DOE.rst.txt new file mode 100644 index 0000000..e8457e7 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.utils.factorial_DOE.rst.txt @@ -0,0 +1,6 @@ +factorial\_DOE +============== + +.. currentmodule:: obsidian.experiment.utils + +.. autofunction:: factorial_DOE \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.experiment.utils.rst.txt b/_sources/build/apidocs/obsidian.experiment.utils.rst.txt new file mode 100644 index 0000000..4666cf4 --- /dev/null +++ b/_sources/build/apidocs/obsidian.experiment.utils.rst.txt @@ -0,0 +1,18 @@ +utils +===== + +.. automodule:: obsidian.experiment.utils + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + :toctree: + + ~factorial_DOE + + + + + diff --git a/_sources/build/apidocs/obsidian.objectives.base.rst.txt b/_sources/build/apidocs/obsidian.objectives.base.rst.txt new file mode 100644 index 0000000..5e01d86 --- /dev/null +++ b/_sources/build/apidocs/obsidian.objectives.base.rst.txt @@ -0,0 +1,16 @@ +base +==== + +.. automodule:: obsidian.objectives.base + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + + ~Objective + + + + diff --git a/_sources/build/apidocs/obsidian.objectives.custom.rst.txt b/_sources/build/apidocs/obsidian.objectives.custom.rst.txt new file mode 100644 index 0000000..e4baf56 --- /dev/null +++ b/_sources/build/apidocs/obsidian.objectives.custom.rst.txt @@ -0,0 +1,20 @@ +custom +====== + +.. automodule:: obsidian.objectives.custom + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + + ~Bounded_Target + ~Feature_Objective + ~Identity_Objective + ~Index_Objective + ~Utopian_Distance + + + + diff --git a/_sources/build/apidocs/obsidian.objectives.rst.txt b/_sources/build/apidocs/obsidian.objectives.rst.txt new file mode 100644 index 0000000..17e344b --- /dev/null +++ b/_sources/build/apidocs/obsidian.objectives.rst.txt @@ -0,0 +1,26 @@ +objectives +========== + +.. automodule:: obsidian.objectives + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module_shallow.rst + :recursive: + + + base + + + custom + + scalarize + + sequence + diff --git a/_sources/build/apidocs/obsidian.objectives.scalarize.rst.txt b/_sources/build/apidocs/obsidian.objectives.scalarize.rst.txt new file mode 100644 index 0000000..bf11994 --- /dev/null +++ b/_sources/build/apidocs/obsidian.objectives.scalarize.rst.txt @@ -0,0 +1,19 @@ +scalarize +========= + +.. automodule:: obsidian.objectives.scalarize + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + + ~Scalar_Chebyshev + ~Scalar_WeightedNorm + ~Scalar_WeightedSum + ~Scalarization + + + + diff --git a/_sources/build/apidocs/obsidian.objectives.sequence.rst.txt b/_sources/build/apidocs/obsidian.objectives.sequence.rst.txt new file mode 100644 index 0000000..a8c4d57 --- /dev/null +++ b/_sources/build/apidocs/obsidian.objectives.sequence.rst.txt @@ -0,0 +1,16 @@ +sequence +======== + +.. automodule:: obsidian.objectives.sequence + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + + ~Objective_Sequence + + + + diff --git a/_sources/build/apidocs/obsidian.optimizer.base.Optimizer.rst.txt b/_sources/build/apidocs/obsidian.optimizer.base.Optimizer.rst.txt new file mode 100644 index 0000000..f88f237 --- /dev/null +++ b/_sources/build/apidocs/obsidian.optimizer.base.Optimizer.rst.txt @@ -0,0 +1,31 @@ +Optimizer +========= + +.. currentmodule:: obsidian.optimizer.base + +.. autoclass:: Optimizer + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Optimizer.__init__ + ~Optimizer.fit + ~Optimizer.hypervolume + ~Optimizer.load_state + ~Optimizer.maximize + ~Optimizer.pareto + ~Optimizer.pf_distance + ~Optimizer.predict + ~Optimizer.save_state + ~Optimizer.suggest + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.optimizer.base.rst.txt b/_sources/build/apidocs/obsidian.optimizer.base.rst.txt new file mode 100644 index 0000000..f35a0e4 --- /dev/null +++ b/_sources/build/apidocs/obsidian.optimizer.base.rst.txt @@ -0,0 +1,18 @@ +base +==== + +.. automodule:: obsidian.optimizer.base + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~Optimizer + + + + + diff --git a/_sources/build/apidocs/obsidian.optimizer.bayesian.BayesianOptimizer.rst.txt b/_sources/build/apidocs/obsidian.optimizer.bayesian.BayesianOptimizer.rst.txt new file mode 100644 index 0000000..47432b8 --- /dev/null +++ b/_sources/build/apidocs/obsidian.optimizer.bayesian.BayesianOptimizer.rst.txt @@ -0,0 +1,38 @@ +BayesianOptimizer +================= + +.. currentmodule:: obsidian.optimizer.bayesian + +.. autoclass:: BayesianOptimizer + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~BayesianOptimizer.__init__ + ~BayesianOptimizer.evaluate + ~BayesianOptimizer.fit + ~BayesianOptimizer.hypervolume + ~BayesianOptimizer.load_state + ~BayesianOptimizer.maximize + ~BayesianOptimizer.pareto + ~BayesianOptimizer.pf_distance + ~BayesianOptimizer.predict + ~BayesianOptimizer.save_state + ~BayesianOptimizer.suggest + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~BayesianOptimizer.is_fit + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.optimizer.bayesian.rst.txt b/_sources/build/apidocs/obsidian.optimizer.bayesian.rst.txt new file mode 100644 index 0000000..1080825 --- /dev/null +++ b/_sources/build/apidocs/obsidian.optimizer.bayesian.rst.txt @@ -0,0 +1,18 @@ +bayesian +======== + +.. automodule:: obsidian.optimizer.bayesian + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~BayesianOptimizer + + + + + diff --git a/_sources/build/apidocs/obsidian.optimizer.rst.txt b/_sources/build/apidocs/obsidian.optimizer.rst.txt new file mode 100644 index 0000000..10f1a48 --- /dev/null +++ b/_sources/build/apidocs/obsidian.optimizer.rst.txt @@ -0,0 +1,22 @@ +optimizer +========= + +.. automodule:: obsidian.optimizer + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module.rst + :recursive: + + + base + + bayesian + + diff --git a/_sources/build/apidocs/obsidian.parameters.base.Parameter.rst.txt b/_sources/build/apidocs/obsidian.parameters.base.Parameter.rst.txt new file mode 100644 index 0000000..d3f17ba --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.base.Parameter.rst.txt @@ -0,0 +1,26 @@ +Parameter +========= + +.. currentmodule:: obsidian.parameters.base + +.. autoclass:: Parameter + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Parameter.__init__ + ~Parameter.decode + ~Parameter.encode + ~Parameter.load_state + ~Parameter.save_state + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.base.rst.txt b/_sources/build/apidocs/obsidian.parameters.base.rst.txt new file mode 100644 index 0000000..78f6b37 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.base.rst.txt @@ -0,0 +1,18 @@ +base +==== + +.. automodule:: obsidian.parameters.base + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~Parameter + + + + + diff --git a/_sources/build/apidocs/obsidian.parameters.continuous.Param_Continuous.rst.txt b/_sources/build/apidocs/obsidian.parameters.continuous.Param_Continuous.rst.txt new file mode 100644 index 0000000..84f1dde --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.continuous.Param_Continuous.rst.txt @@ -0,0 +1,34 @@ +Param\_Continuous +================= + +.. currentmodule:: obsidian.parameters.continuous + +.. autoclass:: Param_Continuous + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Param_Continuous.__init__ + ~Param_Continuous.decode + ~Param_Continuous.encode + ~Param_Continuous.load_state + ~Param_Continuous.save_state + ~Param_Continuous.unit_demap + ~Param_Continuous.unit_map + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~Param_Continuous.range + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.continuous.Param_Observational.rst.txt b/_sources/build/apidocs/obsidian.parameters.continuous.Param_Observational.rst.txt new file mode 100644 index 0000000..912ae94 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.continuous.Param_Observational.rst.txt @@ -0,0 +1,34 @@ +Param\_Observational +==================== + +.. currentmodule:: obsidian.parameters.continuous + +.. autoclass:: Param_Observational + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Param_Observational.__init__ + ~Param_Observational.decode + ~Param_Observational.encode + ~Param_Observational.load_state + ~Param_Observational.save_state + ~Param_Observational.unit_demap + ~Param_Observational.unit_map + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~Param_Observational.range + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.continuous.rst.txt b/_sources/build/apidocs/obsidian.parameters.continuous.rst.txt new file mode 100644 index 0000000..19873c2 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.continuous.rst.txt @@ -0,0 +1,19 @@ +continuous +========== + +.. automodule:: obsidian.parameters.continuous + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~Param_Continuous + ~Param_Observational + + + + + diff --git a/_sources/build/apidocs/obsidian.parameters.discrete.Param_Categorical.rst.txt b/_sources/build/apidocs/obsidian.parameters.discrete.Param_Categorical.rst.txt new file mode 100644 index 0000000..d31cf51 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.discrete.Param_Categorical.rst.txt @@ -0,0 +1,36 @@ +Param\_Categorical +================== + +.. currentmodule:: obsidian.parameters.discrete + +.. autoclass:: Param_Categorical + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Param_Categorical.__init__ + ~Param_Categorical.decode + ~Param_Categorical.encode + ~Param_Categorical.load_state + ~Param_Categorical.save_state + ~Param_Categorical.unit_demap + ~Param_Categorical.unit_map + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~Param_Categorical.max + ~Param_Categorical.min + ~Param_Categorical.nc + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.discrete.Param_Discrete.rst.txt b/_sources/build/apidocs/obsidian.parameters.discrete.Param_Discrete.rst.txt new file mode 100644 index 0000000..994a948 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.discrete.Param_Discrete.rst.txt @@ -0,0 +1,36 @@ +Param\_Discrete +=============== + +.. currentmodule:: obsidian.parameters.discrete + +.. autoclass:: Param_Discrete + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Param_Discrete.__init__ + ~Param_Discrete.decode + ~Param_Discrete.encode + ~Param_Discrete.load_state + ~Param_Discrete.save_state + ~Param_Discrete.unit_demap + ~Param_Discrete.unit_map + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~Param_Discrete.max + ~Param_Discrete.min + ~Param_Discrete.nc + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.discrete.Param_Discrete_Numeric.rst.txt b/_sources/build/apidocs/obsidian.parameters.discrete.Param_Discrete_Numeric.rst.txt new file mode 100644 index 0000000..147fbfd --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.discrete.Param_Discrete_Numeric.rst.txt @@ -0,0 +1,37 @@ +Param\_Discrete\_Numeric +======================== + +.. currentmodule:: obsidian.parameters.discrete + +.. autoclass:: Param_Discrete_Numeric + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Param_Discrete_Numeric.__init__ + ~Param_Discrete_Numeric.decode + ~Param_Discrete_Numeric.encode + ~Param_Discrete_Numeric.load_state + ~Param_Discrete_Numeric.save_state + ~Param_Discrete_Numeric.unit_demap + ~Param_Discrete_Numeric.unit_map + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~Param_Discrete_Numeric.max + ~Param_Discrete_Numeric.min + ~Param_Discrete_Numeric.nc + ~Param_Discrete_Numeric.range + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.discrete.Param_Ordinal.rst.txt b/_sources/build/apidocs/obsidian.parameters.discrete.Param_Ordinal.rst.txt new file mode 100644 index 0000000..78e0727 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.discrete.Param_Ordinal.rst.txt @@ -0,0 +1,36 @@ +Param\_Ordinal +============== + +.. currentmodule:: obsidian.parameters.discrete + +.. autoclass:: Param_Ordinal + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Param_Ordinal.__init__ + ~Param_Ordinal.decode + ~Param_Ordinal.encode + ~Param_Ordinal.load_state + ~Param_Ordinal.save_state + ~Param_Ordinal.unit_demap + ~Param_Ordinal.unit_map + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~Param_Ordinal.max + ~Param_Ordinal.min + ~Param_Ordinal.nc + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.discrete.Task.rst.txt b/_sources/build/apidocs/obsidian.parameters.discrete.Task.rst.txt new file mode 100644 index 0000000..7645dcc --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.discrete.Task.rst.txt @@ -0,0 +1,36 @@ +Task +==== + +.. currentmodule:: obsidian.parameters.discrete + +.. autoclass:: Task + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Task.__init__ + ~Task.decode + ~Task.encode + ~Task.load_state + ~Task.save_state + ~Task.unit_demap + ~Task.unit_map + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~Task.max + ~Task.min + ~Task.nc + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.discrete.rst.txt b/_sources/build/apidocs/obsidian.parameters.discrete.rst.txt new file mode 100644 index 0000000..5ed5450 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.discrete.rst.txt @@ -0,0 +1,22 @@ +discrete +======== + +.. automodule:: obsidian.parameters.discrete + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~Param_Categorical + ~Param_Discrete + ~Param_Discrete_Numeric + ~Param_Ordinal + ~Task + + + + + diff --git a/_sources/build/apidocs/obsidian.parameters.param_space.ParamSpace.rst.txt b/_sources/build/apidocs/obsidian.parameters.param_space.ParamSpace.rst.txt new file mode 100644 index 0000000..14b211c --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.param_space.ParamSpace.rst.txt @@ -0,0 +1,31 @@ +ParamSpace +========== + +.. currentmodule:: obsidian.parameters.param_space + +.. autoclass:: ParamSpace + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~ParamSpace.__init__ + ~ParamSpace.decode + ~ParamSpace.encode + ~ParamSpace.load_state + ~ParamSpace.map_inv_transform + ~ParamSpace.map_transform + ~ParamSpace.mean + ~ParamSpace.save_state + ~ParamSpace.unit_demap + ~ParamSpace.unit_map + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.param_space.rst.txt b/_sources/build/apidocs/obsidian.parameters.param_space.rst.txt new file mode 100644 index 0000000..5aa278c --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.param_space.rst.txt @@ -0,0 +1,18 @@ +param\_space +============ + +.. automodule:: obsidian.parameters.param_space + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~ParamSpace + + + + + diff --git a/_sources/build/apidocs/obsidian.parameters.rst.txt b/_sources/build/apidocs/obsidian.parameters.rst.txt new file mode 100644 index 0000000..bcfd53b --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.rst.txt @@ -0,0 +1,32 @@ +parameters +========== + +.. automodule:: obsidian.parameters + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module.rst + :recursive: + + + base + + continuous + + discrete + + param_space + + targets + + transforms + + utils + + diff --git a/_sources/build/apidocs/obsidian.parameters.targets.Target.rst.txt b/_sources/build/apidocs/obsidian.parameters.targets.Target.rst.txt new file mode 100644 index 0000000..0be8829 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.targets.Target.rst.txt @@ -0,0 +1,25 @@ +Target +====== + +.. currentmodule:: obsidian.parameters.targets + +.. autoclass:: Target + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Target.__init__ + ~Target.load_state + ~Target.save_state + ~Target.transform_f + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.targets.rst.txt b/_sources/build/apidocs/obsidian.parameters.targets.rst.txt new file mode 100644 index 0000000..a213ce7 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.targets.rst.txt @@ -0,0 +1,18 @@ +targets +======= + +.. automodule:: obsidian.parameters.targets + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~Target + + + + + diff --git a/_sources/build/apidocs/obsidian.parameters.transforms.Identity_Scaler.rst.txt b/_sources/build/apidocs/obsidian.parameters.transforms.Identity_Scaler.rst.txt new file mode 100644 index 0000000..7b63791 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.transforms.Identity_Scaler.rst.txt @@ -0,0 +1,24 @@ +Identity\_Scaler +================ + +.. currentmodule:: obsidian.parameters.transforms + +.. autoclass:: Identity_Scaler + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Identity_Scaler.__init__ + ~Identity_Scaler.forward + ~Identity_Scaler.inverse + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.transforms.Logit_Scaler.rst.txt b/_sources/build/apidocs/obsidian.parameters.transforms.Logit_Scaler.rst.txt new file mode 100644 index 0000000..8e63f67 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.transforms.Logit_Scaler.rst.txt @@ -0,0 +1,24 @@ +Logit\_Scaler +============= + +.. currentmodule:: obsidian.parameters.transforms + +.. autoclass:: Logit_Scaler + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Logit_Scaler.__init__ + ~Logit_Scaler.forward + ~Logit_Scaler.inverse + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.transforms.Standard_Scaler.rst.txt b/_sources/build/apidocs/obsidian.parameters.transforms.Standard_Scaler.rst.txt new file mode 100644 index 0000000..5d5fb1a --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.transforms.Standard_Scaler.rst.txt @@ -0,0 +1,24 @@ +Standard\_Scaler +================ + +.. currentmodule:: obsidian.parameters.transforms + +.. autoclass:: Standard_Scaler + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Standard_Scaler.__init__ + ~Standard_Scaler.forward + ~Standard_Scaler.inverse + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.transforms.Target_Transform.rst.txt b/_sources/build/apidocs/obsidian.parameters.transforms.Target_Transform.rst.txt new file mode 100644 index 0000000..f4c12a4 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.transforms.Target_Transform.rst.txt @@ -0,0 +1,24 @@ +Target\_Transform +================= + +.. currentmodule:: obsidian.parameters.transforms + +.. autoclass:: Target_Transform + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~Target_Transform.__init__ + ~Target_Transform.forward + ~Target_Transform.inverse + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.parameters.transforms.rst.txt b/_sources/build/apidocs/obsidian.parameters.transforms.rst.txt new file mode 100644 index 0000000..edde7a1 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.transforms.rst.txt @@ -0,0 +1,21 @@ +transforms +========== + +.. automodule:: obsidian.parameters.transforms + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~Identity_Scaler + ~Logit_Scaler + ~Standard_Scaler + ~Target_Transform + + + + + diff --git a/_sources/build/apidocs/obsidian.parameters.utils.rst.txt b/_sources/build/apidocs/obsidian.parameters.utils.rst.txt new file mode 100644 index 0000000..3c29355 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.utils.rst.txt @@ -0,0 +1,18 @@ +utils +===== + +.. automodule:: obsidian.parameters.utils + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + :toctree: + + ~transform_with_type + + + + + diff --git a/_sources/build/apidocs/obsidian.parameters.utils.transform_with_type.rst.txt b/_sources/build/apidocs/obsidian.parameters.utils.transform_with_type.rst.txt new file mode 100644 index 0000000..49c3935 --- /dev/null +++ b/_sources/build/apidocs/obsidian.parameters.utils.transform_with_type.rst.txt @@ -0,0 +1,6 @@ +transform\_with\_type +===================== + +.. currentmodule:: obsidian.parameters.utils + +.. autofunction:: transform_with_type \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.plotting.branding.rst.txt b/_sources/build/apidocs/obsidian.plotting.branding.rst.txt new file mode 100644 index 0000000..5d2dfbc --- /dev/null +++ b/_sources/build/apidocs/obsidian.plotting.branding.rst.txt @@ -0,0 +1,25 @@ +branding +======== + +.. automodule:: obsidian.plotting.branding + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + + ~hex_to_rgb + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + + ~ColorMaps + ~ColorScheme + ~Palette + + + + diff --git a/_sources/build/apidocs/obsidian.plotting.mpl.rst.txt b/_sources/build/apidocs/obsidian.plotting.mpl.rst.txt new file mode 100644 index 0000000..b9e71ef --- /dev/null +++ b/_sources/build/apidocs/obsidian.plotting.mpl.rst.txt @@ -0,0 +1,16 @@ +mpl +=== + +.. automodule:: obsidian.plotting.mpl + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + + ~visualize_inputs + + + + diff --git a/_sources/build/apidocs/obsidian.plotting.plotly.rst.txt b/_sources/build/apidocs/obsidian.plotting.plotly.rst.txt new file mode 100644 index 0000000..8173dd5 --- /dev/null +++ b/_sources/build/apidocs/obsidian.plotting.plotly.rst.txt @@ -0,0 +1,19 @@ +plotly +====== + +.. automodule:: obsidian.plotting.plotly + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + + ~MOO_results + ~factor_plot + ~parity_plot + ~surface_plot + + + + diff --git a/_sources/build/apidocs/obsidian.plotting.rst.txt b/_sources/build/apidocs/obsidian.plotting.rst.txt new file mode 100644 index 0000000..7dd5d9c --- /dev/null +++ b/_sources/build/apidocs/obsidian.plotting.rst.txt @@ -0,0 +1,25 @@ +plotting +======== + +.. automodule:: obsidian.plotting + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module_shallow.rst + :recursive: + + + branding + + mpl + + plotly + + shap + diff --git a/_sources/build/apidocs/obsidian.plotting.shap.rst.txt b/_sources/build/apidocs/obsidian.plotting.shap.rst.txt new file mode 100644 index 0000000..caae973 --- /dev/null +++ b/_sources/build/apidocs/obsidian.plotting.shap.rst.txt @@ -0,0 +1,17 @@ +shap +==== + +.. automodule:: obsidian.plotting.shap + + + .. rubric:: Functions + + .. autosummary:: + :template: base.rst + + ~one_shap_value + ~partial_dependence + + + + diff --git a/_sources/build/apidocs/obsidian.surrogates.base.SurrogateModel.rst.txt b/_sources/build/apidocs/obsidian.surrogates.base.SurrogateModel.rst.txt new file mode 100644 index 0000000..2c9d897 --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.base.SurrogateModel.rst.txt @@ -0,0 +1,27 @@ +SurrogateModel +============== + +.. currentmodule:: obsidian.surrogates.base + +.. autoclass:: SurrogateModel + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~SurrogateModel.__init__ + ~SurrogateModel.fit + ~SurrogateModel.load_state + ~SurrogateModel.predict + ~SurrogateModel.save_state + ~SurrogateModel.score + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.surrogates.base.rst.txt b/_sources/build/apidocs/obsidian.surrogates.base.rst.txt new file mode 100644 index 0000000..8a99556 --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.base.rst.txt @@ -0,0 +1,18 @@ +base +==== + +.. automodule:: obsidian.surrogates.base + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~SurrogateModel + + + + + diff --git a/_sources/build/apidocs/obsidian.surrogates.botorch.SurrogateBoTorch.rst.txt b/_sources/build/apidocs/obsidian.surrogates.botorch.SurrogateBoTorch.rst.txt new file mode 100644 index 0000000..5e461c3 --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.botorch.SurrogateBoTorch.rst.txt @@ -0,0 +1,28 @@ +SurrogateBoTorch +================ + +.. currentmodule:: obsidian.surrogates.botorch + +.. autoclass:: SurrogateBoTorch + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~SurrogateBoTorch.__init__ + ~SurrogateBoTorch.fit + ~SurrogateBoTorch.init_model + ~SurrogateBoTorch.load_state + ~SurrogateBoTorch.predict + ~SurrogateBoTorch.save_state + ~SurrogateBoTorch.score + + + + + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.surrogates.botorch.rst.txt b/_sources/build/apidocs/obsidian.surrogates.botorch.rst.txt new file mode 100644 index 0000000..8983567 --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.botorch.rst.txt @@ -0,0 +1,18 @@ +botorch +======= + +.. automodule:: obsidian.surrogates.botorch + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~SurrogateBoTorch + + + + + diff --git a/_sources/build/apidocs/obsidian.surrogates.custom_GP.DKLGP.rst.txt b/_sources/build/apidocs/obsidian.surrogates.custom_GP.DKLGP.rst.txt new file mode 100644 index 0000000..754ee8d --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.custom_GP.DKLGP.rst.txt @@ -0,0 +1,112 @@ +DKLGP +===== + +.. currentmodule:: obsidian.surrogates.custom_GP + +.. autoclass:: DKLGP + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~DKLGP.__init__ + ~DKLGP.add_module + ~DKLGP.added_loss_terms + ~DKLGP.apply + ~DKLGP.bfloat16 + ~DKLGP.buffers + ~DKLGP.children + ~DKLGP.compile + ~DKLGP.condition_on_observations + ~DKLGP.constraint_for_parameter_name + ~DKLGP.constraints + ~DKLGP.construct_inputs + ~DKLGP.cpu + ~DKLGP.cuda + ~DKLGP.double + ~DKLGP.eval + ~DKLGP.extra_repr + ~DKLGP.float + ~DKLGP.forward + ~DKLGP.get_buffer + ~DKLGP.get_extra_state + ~DKLGP.get_fantasy_model + ~DKLGP.get_parameter + ~DKLGP.get_submodule + ~DKLGP.half + ~DKLGP.hyperparameters + ~DKLGP.initialize + ~DKLGP.ipu + ~DKLGP.load_state_dict + ~DKLGP.load_strict_shapes + ~DKLGP.local_load_samples + ~DKLGP.modules + ~DKLGP.named_added_loss_terms + ~DKLGP.named_buffers + ~DKLGP.named_children + ~DKLGP.named_constraints + ~DKLGP.named_hyperparameters + ~DKLGP.named_modules + ~DKLGP.named_parameters + ~DKLGP.named_parameters_and_constraints + ~DKLGP.named_priors + ~DKLGP.named_variational_parameters + ~DKLGP.parameters + ~DKLGP.posterior + ~DKLGP.pyro_load_from_samples + ~DKLGP.pyro_sample_from_prior + ~DKLGP.register_added_loss_term + ~DKLGP.register_backward_hook + ~DKLGP.register_buffer + ~DKLGP.register_constraint + ~DKLGP.register_forward_hook + ~DKLGP.register_forward_pre_hook + ~DKLGP.register_full_backward_hook + ~DKLGP.register_full_backward_pre_hook + ~DKLGP.register_load_state_dict_post_hook + ~DKLGP.register_module + ~DKLGP.register_parameter + ~DKLGP.register_prior + ~DKLGP.register_state_dict_pre_hook + ~DKLGP.requires_grad_ + ~DKLGP.sample_from_prior + ~DKLGP.set_extra_state + ~DKLGP.set_train_data + ~DKLGP.share_memory + ~DKLGP.state_dict + ~DKLGP.subset_output + ~DKLGP.to + ~DKLGP.to_empty + ~DKLGP.to_pyro_random_module + ~DKLGP.to_random_module + ~DKLGP.train + ~DKLGP.transform_inputs + ~DKLGP.type + ~DKLGP.update_added_loss_term + ~DKLGP.variational_parameters + ~DKLGP.xpu + ~DKLGP.zero_grad + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~DKLGP.T_destination + ~DKLGP.batch_shape + ~DKLGP.call_super_init + ~DKLGP.dtypes_of_buffers + ~DKLGP.dump_patches + ~DKLGP.num_outputs + ~DKLGP.train_targets + ~DKLGP.likelihood + ~DKLGP.training + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.surrogates.custom_GP.FlatGP.rst.txt b/_sources/build/apidocs/obsidian.surrogates.custom_GP.FlatGP.rst.txt new file mode 100644 index 0000000..e6427a8 --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.custom_GP.FlatGP.rst.txt @@ -0,0 +1,112 @@ +FlatGP +====== + +.. currentmodule:: obsidian.surrogates.custom_GP + +.. autoclass:: FlatGP + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~FlatGP.__init__ + ~FlatGP.add_module + ~FlatGP.added_loss_terms + ~FlatGP.apply + ~FlatGP.bfloat16 + ~FlatGP.buffers + ~FlatGP.children + ~FlatGP.compile + ~FlatGP.condition_on_observations + ~FlatGP.constraint_for_parameter_name + ~FlatGP.constraints + ~FlatGP.construct_inputs + ~FlatGP.cpu + ~FlatGP.cuda + ~FlatGP.double + ~FlatGP.eval + ~FlatGP.extra_repr + ~FlatGP.float + ~FlatGP.forward + ~FlatGP.get_buffer + ~FlatGP.get_extra_state + ~FlatGP.get_fantasy_model + ~FlatGP.get_parameter + ~FlatGP.get_submodule + ~FlatGP.half + ~FlatGP.hyperparameters + ~FlatGP.initialize + ~FlatGP.ipu + ~FlatGP.load_state_dict + ~FlatGP.load_strict_shapes + ~FlatGP.local_load_samples + ~FlatGP.modules + ~FlatGP.named_added_loss_terms + ~FlatGP.named_buffers + ~FlatGP.named_children + ~FlatGP.named_constraints + ~FlatGP.named_hyperparameters + ~FlatGP.named_modules + ~FlatGP.named_parameters + ~FlatGP.named_parameters_and_constraints + ~FlatGP.named_priors + ~FlatGP.named_variational_parameters + ~FlatGP.parameters + ~FlatGP.posterior + ~FlatGP.pyro_load_from_samples + ~FlatGP.pyro_sample_from_prior + ~FlatGP.register_added_loss_term + ~FlatGP.register_backward_hook + ~FlatGP.register_buffer + ~FlatGP.register_constraint + ~FlatGP.register_forward_hook + ~FlatGP.register_forward_pre_hook + ~FlatGP.register_full_backward_hook + ~FlatGP.register_full_backward_pre_hook + ~FlatGP.register_load_state_dict_post_hook + ~FlatGP.register_module + ~FlatGP.register_parameter + ~FlatGP.register_prior + ~FlatGP.register_state_dict_pre_hook + ~FlatGP.requires_grad_ + ~FlatGP.sample_from_prior + ~FlatGP.set_extra_state + ~FlatGP.set_train_data + ~FlatGP.share_memory + ~FlatGP.state_dict + ~FlatGP.subset_output + ~FlatGP.to + ~FlatGP.to_empty + ~FlatGP.to_pyro_random_module + ~FlatGP.to_random_module + ~FlatGP.train + ~FlatGP.transform_inputs + ~FlatGP.type + ~FlatGP.update_added_loss_term + ~FlatGP.variational_parameters + ~FlatGP.xpu + ~FlatGP.zero_grad + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~FlatGP.T_destination + ~FlatGP.batch_shape + ~FlatGP.call_super_init + ~FlatGP.dtypes_of_buffers + ~FlatGP.dump_patches + ~FlatGP.num_outputs + ~FlatGP.train_targets + ~FlatGP.likelihood + ~FlatGP.training + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.surrogates.custom_GP.PriorGP.rst.txt b/_sources/build/apidocs/obsidian.surrogates.custom_GP.PriorGP.rst.txt new file mode 100644 index 0000000..8c69442 --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.custom_GP.PriorGP.rst.txt @@ -0,0 +1,112 @@ +PriorGP +======= + +.. currentmodule:: obsidian.surrogates.custom_GP + +.. autoclass:: PriorGP + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~PriorGP.__init__ + ~PriorGP.add_module + ~PriorGP.added_loss_terms + ~PriorGP.apply + ~PriorGP.bfloat16 + ~PriorGP.buffers + ~PriorGP.children + ~PriorGP.compile + ~PriorGP.condition_on_observations + ~PriorGP.constraint_for_parameter_name + ~PriorGP.constraints + ~PriorGP.construct_inputs + ~PriorGP.cpu + ~PriorGP.cuda + ~PriorGP.double + ~PriorGP.eval + ~PriorGP.extra_repr + ~PriorGP.float + ~PriorGP.forward + ~PriorGP.get_buffer + ~PriorGP.get_extra_state + ~PriorGP.get_fantasy_model + ~PriorGP.get_parameter + ~PriorGP.get_submodule + ~PriorGP.half + ~PriorGP.hyperparameters + ~PriorGP.initialize + ~PriorGP.ipu + ~PriorGP.load_state_dict + ~PriorGP.load_strict_shapes + ~PriorGP.local_load_samples + ~PriorGP.modules + ~PriorGP.named_added_loss_terms + ~PriorGP.named_buffers + ~PriorGP.named_children + ~PriorGP.named_constraints + ~PriorGP.named_hyperparameters + ~PriorGP.named_modules + ~PriorGP.named_parameters + ~PriorGP.named_parameters_and_constraints + ~PriorGP.named_priors + ~PriorGP.named_variational_parameters + ~PriorGP.parameters + ~PriorGP.posterior + ~PriorGP.pyro_load_from_samples + ~PriorGP.pyro_sample_from_prior + ~PriorGP.register_added_loss_term + ~PriorGP.register_backward_hook + ~PriorGP.register_buffer + ~PriorGP.register_constraint + ~PriorGP.register_forward_hook + ~PriorGP.register_forward_pre_hook + ~PriorGP.register_full_backward_hook + ~PriorGP.register_full_backward_pre_hook + ~PriorGP.register_load_state_dict_post_hook + ~PriorGP.register_module + ~PriorGP.register_parameter + ~PriorGP.register_prior + ~PriorGP.register_state_dict_pre_hook + ~PriorGP.requires_grad_ + ~PriorGP.sample_from_prior + ~PriorGP.set_extra_state + ~PriorGP.set_train_data + ~PriorGP.share_memory + ~PriorGP.state_dict + ~PriorGP.subset_output + ~PriorGP.to + ~PriorGP.to_empty + ~PriorGP.to_pyro_random_module + ~PriorGP.to_random_module + ~PriorGP.train + ~PriorGP.transform_inputs + ~PriorGP.type + ~PriorGP.update_added_loss_term + ~PriorGP.variational_parameters + ~PriorGP.xpu + ~PriorGP.zero_grad + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~PriorGP.T_destination + ~PriorGP.batch_shape + ~PriorGP.call_super_init + ~PriorGP.dtypes_of_buffers + ~PriorGP.dump_patches + ~PriorGP.num_outputs + ~PriorGP.train_targets + ~PriorGP.likelihood + ~PriorGP.training + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.surrogates.custom_GP.rst.txt b/_sources/build/apidocs/obsidian.surrogates.custom_GP.rst.txt new file mode 100644 index 0000000..3f54001 --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.custom_GP.rst.txt @@ -0,0 +1,20 @@ +custom\_GP +========== + +.. automodule:: obsidian.surrogates.custom_GP + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~DKLGP + ~FlatGP + ~PriorGP + + + + + diff --git a/_sources/build/apidocs/obsidian.surrogates.custom_torch.DNN.rst.txt b/_sources/build/apidocs/obsidian.surrogates.custom_torch.DNN.rst.txt new file mode 100644 index 0000000..153ff5f --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.custom_torch.DNN.rst.txt @@ -0,0 +1,85 @@ +DNN +=== + +.. currentmodule:: obsidian.surrogates.custom_torch + +.. autoclass:: DNN + + + .. automethod:: __init__ + + + .. rubric:: Methods + + .. autosummary:: + + ~DNN.__init__ + ~DNN.add_module + ~DNN.apply + ~DNN.bfloat16 + ~DNN.buffers + ~DNN.children + ~DNN.compile + ~DNN.condition_on_observations + ~DNN.construct_inputs + ~DNN.cpu + ~DNN.cuda + ~DNN.double + ~DNN.eval + ~DNN.extra_repr + ~DNN.float + ~DNN.forward + ~DNN.get_buffer + ~DNN.get_extra_state + ~DNN.get_parameter + ~DNN.get_submodule + ~DNN.half + ~DNN.ipu + ~DNN.load_state_dict + ~DNN.modules + ~DNN.named_buffers + ~DNN.named_children + ~DNN.named_modules + ~DNN.named_parameters + ~DNN.parameters + ~DNN.posterior + ~DNN.register_backward_hook + ~DNN.register_buffer + ~DNN.register_forward_hook + ~DNN.register_forward_pre_hook + ~DNN.register_full_backward_hook + ~DNN.register_full_backward_pre_hook + ~DNN.register_load_state_dict_post_hook + ~DNN.register_module + ~DNN.register_parameter + ~DNN.register_state_dict_pre_hook + ~DNN.requires_grad_ + ~DNN.set_extra_state + ~DNN.share_memory + ~DNN.state_dict + ~DNN.subset_output + ~DNN.to + ~DNN.to_empty + ~DNN.train + ~DNN.transform_inputs + ~DNN.type + ~DNN.xpu + ~DNN.zero_grad + + + + + + .. rubric:: Attributes + + .. autosummary:: + + ~DNN.T_destination + ~DNN.batch_shape + ~DNN.call_super_init + ~DNN.dtypes_of_buffers + ~DNN.dump_patches + ~DNN.num_outputs + ~DNN.training + + \ No newline at end of file diff --git a/_sources/build/apidocs/obsidian.surrogates.custom_torch.rst.txt b/_sources/build/apidocs/obsidian.surrogates.custom_torch.rst.txt new file mode 100644 index 0000000..3770532 --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.custom_torch.rst.txt @@ -0,0 +1,18 @@ +custom\_torch +============= + +.. automodule:: obsidian.surrogates.custom_torch + + + .. rubric:: Classes + + .. autosummary:: + :template: class.rst + :toctree: + + ~DNN + + + + + diff --git a/_sources/build/apidocs/obsidian.surrogates.rst.txt b/_sources/build/apidocs/obsidian.surrogates.rst.txt new file mode 100644 index 0000000..de96834 --- /dev/null +++ b/_sources/build/apidocs/obsidian.surrogates.rst.txt @@ -0,0 +1,27 @@ +surrogates +========== + +.. automodule:: obsidian.surrogates + + + + + +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: module.rst + :recursive: + + + base + + botorch + + + custom_GP + + custom_torch + + diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000..62a7540 --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,22 @@ +.. toctree:: + :hidden: + :maxdepth: 2 + + GitHub + + stubs/api_docs + +.. toctree:: + :hidden: + :maxdepth: 1 + :titlesonly: + :caption: Links + + stubs/wiki + + stubs/tutorials + + stubs/reference + +.. include:: ../readme.md + :parser: myst_parser.sphinx_ \ No newline at end of file diff --git a/_sources/stubs/api_docs.rst.txt b/_sources/stubs/api_docs.rst.txt new file mode 100644 index 0000000..c82cd64 --- /dev/null +++ b/_sources/stubs/api_docs.rst.txt @@ -0,0 +1,44 @@ +API Documentation +================= + +.. automodule:: obsidian + + +.. rubric:: Modules + +.. autosummary:: + :toctree: ../build/apidocs + :recursive: + :template: module.rst + + obsidian.parameters + + obsidian.campaign + + obsidian.optimizer + + obsidian.surrogates + + obsidian.experiment + +.. autosummary:: + :toctree: ../build/apidocs + :recursive: + :template: module_shallow.rst + + obsidian.acquisition + + obsidian.objectives + + obsidian.plotting + + obsidian.constraints + + obsidian.exceptions + +Indices +~~~~~~~~~~~~~~~~~~ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` \ No newline at end of file diff --git a/_sources/stubs/changelog.rst.txt b/_sources/stubs/changelog.rst.txt new file mode 100644 index 0000000..4d3d32d --- /dev/null +++ b/_sources/stubs/changelog.rst.txt @@ -0,0 +1,2 @@ +.. include:: ../../CHANGELOG.md + :parser: myst_parser.sphinx_ \ No newline at end of file diff --git a/_sources/stubs/contributing.rst.txt b/_sources/stubs/contributing.rst.txt new file mode 100644 index 0000000..3b16b8d --- /dev/null +++ b/_sources/stubs/contributing.rst.txt @@ -0,0 +1,2 @@ +.. include:: ../../CONTRIBUTING.md + :parser: myst_parser.sphinx_ \ No newline at end of file diff --git a/_sources/stubs/license.rst.txt b/_sources/stubs/license.rst.txt new file mode 100644 index 0000000..14685e9 --- /dev/null +++ b/_sources/stubs/license.rst.txt @@ -0,0 +1,5 @@ +License +======== + +.. include:: ../../LICENSE + :parser: myst_parser.sphinx_ \ No newline at end of file diff --git a/_sources/stubs/reference.rst.txt b/_sources/stubs/reference.rst.txt new file mode 100644 index 0000000..29793d7 --- /dev/null +++ b/_sources/stubs/reference.rst.txt @@ -0,0 +1,16 @@ +Reference +=============== + +.. toctree:: + :maxdepth: 1 + + changelog + contributing + license + +Indices +~~~~~~~~~~~~~~~~~~ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` \ No newline at end of file diff --git a/_sources/stubs/tutorials.rst.txt b/_sources/stubs/tutorials.rst.txt new file mode 100644 index 0000000..bfc216a --- /dev/null +++ b/_sources/stubs/tutorials.rst.txt @@ -0,0 +1,15 @@ +Tutorials +========= + +.. toctree:: + :titlesonly: + :glob: + + ../../stubs/tutorials/* + +Indices +~~~~~~~~~~~~~~~~~~ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` \ No newline at end of file diff --git a/_sources/stubs/tutorials/Simple single objective.rst.txt b/_sources/stubs/tutorials/Simple single objective.rst.txt new file mode 100644 index 0000000..88a917f --- /dev/null +++ b/_sources/stubs/tutorials/Simple single objective.rst.txt @@ -0,0 +1,6 @@ + +Simple Single Objective +======================= + +.. raw:: html + :file: ../../_static/tutorials/Simple single objective.html \ No newline at end of file diff --git a/_sources/stubs/wiki.rst.txt b/_sources/stubs/wiki.rst.txt new file mode 100644 index 0000000..6cb5b3b --- /dev/null +++ b/_sources/stubs/wiki.rst.txt @@ -0,0 +1,15 @@ +Wiki +===== + +.. toctree:: + :titlesonly: + :glob: + + ../../wiki/* + +Indices +~~~~~~~~~~~~~~~~~~ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` \ No newline at end of file diff --git a/_sources/wiki/1_APO_Workflow_and_CodeStructure.md.txt b/_sources/wiki/1_APO_Workflow_and_CodeStructure.md.txt new file mode 100644 index 0000000..47f39c2 --- /dev/null +++ b/_sources/wiki/1_APO_Workflow_and_CodeStructure.md.txt @@ -0,0 +1,119 @@ +# Algorithmic Process Optimization (APO) Workflow + +APO is an iterative procedure used for optimizing complex systems with an expensive-to-evaluate objective function. The major steps involve constructing a surrogate model to approximate the objective function and defining an acquisition function based on user preference that explores and/or exploits the design space to find the optimal solution. + +When applied to chemical process optimization, the APO algorithms can effectively identify desirable experimental conditions or parameter settings that maximize process efficiency, minimize costs, or achieve other specified objectives within given resource constraints. + +The components typically involved in APO algorithms are illustrated in the figure below: + +![APO Workflow](https://github.com/MSDLLCpapers/obsidian/blob/main/docs/_static/APO_workflow.png?raw=true) + + +In this **_obsidian_** library: + +The `Optimizer` class object in [optimizer](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/optimizer) submodule is the main portal that connects all the key components in the APO algorithm. + +The [dash](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/dash) submodule contains the source code for a web GUI using the Dash library. + +The [demo](https://github.com/MSDLLCpapers/obsidian/tree/main/demo) folder contains step-by-step Jupyter notebook usage examples as well as scripts for benchmark studies. + + +---- +## Data + +The data structure in a typical APO work flow includes: + +* **$X$: Experimental conditions, also called input features/parameters or independent variables.** Usually a set of <20 variables are chosen to investigate their effects on the experimental outcomes. Various types of variables are supported: + - Continuous variables: + + Continuous (numerical) variable: A variable that can have an infinite number of real values within a given interval (e.g. Temperature) + + Observational (numerical) variable: A variable that can be measured but not directly controlled, which still is useful for model fitting and optimization (e.g. Reaction time) + - Discrete variables: + + Discrete (numerical) variable: A variable that have finite number of numerical values within a given range or list of numbers (e.g. Buffer pKa) + + Ordinal variable: A discrete variable that is defined by a meaningful ordered relationship between different categories (e.g. Light intensity at Low, Medium, High) + + Categorical variable: A discrete variable that describes a name, label, or category without a natural order (e.g. Catalyst name) + + Task variable: A special categorical variable which requires that a distinct response be predicted for each task (e.g. Reactor type) + +* **$X_{space}$: Experimental design space, or input parameter constraints.** The multidimensional parameter space within which the experimental conditions $X$ could be chosen and manipulated. + - For each continuous variable in $X$, the range of min and max range should be specified. + - For each discrete variable, the set of possible distinct values (categories) should be provided. + - Before fitting, all variables in $X_{space}$ are transformed to an encoded, unit-cube space that is amenable to machine-learning, especially for Gaussian Processes. Each variable type has a pre-defined encoder (e.g. min-max scaling for continuous, one-hot encoding for categorical). Optimization and prediction occurs in the encoded (transformed) spsace $X_t$, but de-transformation is always applied at key steps to ensure user-interaction and analysis within the measured parameter space $X$ + +* **$Y$: Experimental outcomes, also called responses or dependent variables.** It is the measured response of a response and the target variable to be optimized when no objective function is defined. Depends on the number of outcomes chosen for optimization, $Y$ could be either a scalar (single-objective optimization) or a vector (multi-objective optimization). For a multi-objective optimization problem, the user can specify whether to maximize or minimize each each element in the $Y$ vector. The responses $Y$ are usually scaled to zero-mean and unit variance $f(Y)$ with or without additional transformation before fitting. + +* **$O(Y, X)$ Objectives, or objective functions.** The target value which is sought to be maximized or minimized in an optimization campaign. Objectives can be defined to convert single-output experiments to multi-output optimizations (e.g. using penalized features), and multi-output experiments can be condensed to signle-output optimizations (e.g. using scalarization or weighting). + + + + +See section +[Data](3_Data.md) +and submodule +[`parameters`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/parameters) +for more details. + + +--- +## Surrogate Model + +Assume the true relationship between the experiment outcome and input is $f(X)$, which is unknown since the random and/or systematic errors $\epsilon$ always present in experiments. + +The measured outcome could be denoted as $Y(X) = f(X) + \epsilon$. When the variance of $\epsilon$ is constant, the noise is homoscedastic; otherwise when the noise is heteroscedastic, where the variance of $\epsilon$ could depend on $X$, making the optimization problem more challenging. + +The surrogate model $\hat{f}(X)$ is a predictive model that approximate the underlying unknown relationship $f(X)$. The $\hat{f}(X)$ has the same dimension as $Y$. +If $Y$ is a vector, the experimental outcomes could be modeled jointly with multivariate multiple regression modeling methods at once; or each outcome could be modeled separately in parallel. + +For Bayesian Optimization, surrogate models should provide both a point prediction (e.g. conditional mean given $X$) as well as a prediction uncertainty metric (e.g. conditional standard deviation given $X$). Most acquisition functions require both point prediction and uncertainty to balance the exploration and exploitation of the design space. + +The most popular choice of surrogate model is Gaussian Process (GP) regression, which is a Bayesian model that combines prior knowledge with observed data to make probabilistic inferences of the posterior distribution. Under the assumption of Gaussian noise $\epsilon$, it is cheap to compute the posterior mean and standard deviation for each new input data. Importantly, GP models do not provide a closed-form expression of $f(X)$ but rather extimate a kernel which describes the covariance between unsampled $X^*$ and sampled $X$ space. Notably suitable for Bayesian Optimization, these kernels typically provide an increasing estimate of uncertainty as the distance from sampled space increases. + +The APO workflow starts with collecting a set of initial experimental data $(X_0,Y_0)$, where the $X_0$ could be random sampled from $X_{space}$ or suggested by design-of-experiments methods. The initial surrogate model $\hat{f}_0$ is then fit based on this initial dataset. +At the $i^{th}$ iteration later, an updated surrogate model $\hat{f}_i$ is trained on all the available data $\{X_t,Y_t\}_{1 \leq t \leq i}$. As more data are accumulated with multiple rounds of optimization, we expect the surrogate function to become more precise and better approximate the actual underlying functional relationship. + +See section +[Surrogate Model](4_SurrogateModel.md) +and submodule +[`surrogates`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/surrogates) +for more details. + + + +--- +## Acquisition Function + +The acquisition function aims to guide the search of promising new experimental conditions with the potential to achieve improved objectives. It usually depends on the surrogate model predictions and/or measured data , which scores any point in the $X_{space}$. + +In the $i^{th}$ iteration, the acquisition function is +```{math} + a_i(X) = a(\hat{f}_i(X), \{X_t,Y_t\}_{1 \leq t \leq i}) +``` + +Maximizing this scoring function over the design space is used to produce the suggested new candidate conditions for the $(i+1)^{th}$ iteration: +\begin{equation*} +X_{i+1} = \underset{X' \in X_{space}}{\text{argmax }} a_i(X') +\end{equation*} + +The definition of acquisition function determines the trade-off between exploration and exploitation based on user preference. + +Exploitation means leveraging existing knowledge by selecting new conditions that lead to optimal outcomes based on surrogate model prediction. It could maximize the immediate gains when the true relationship is relatively well understood and approximated by the surrogate model. + +Exploration involves acquiring new information by sampling from less explored regions of the design space where the prediction uncertainty is higher. It may help to better understand the problem and improve surrogate model quality, which usually leads to better long-term outcomes or reveal new possibilities in the context of global optimization. + + +See section [Acquisition Function](5_AcquisitionFunction.md), +submodule [`acquisition`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/acquisition), +and submodule [`objectives`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/objectives) +for more details. + + +--- +## Suggest New Experimental Conditions + +One or more new candidate experimental condition(s) are suggested based on the criteria of maximizing the acquisition function within the experimental design space. This can be done with individual sequential experiments `m_batch=1` or parallized experiments `m_batch>1` which can be optimized jointly `optim_sequential=False` or sequentially using fantasy models `optim_sequential=True`. More advanced [`constraints`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/constraints) on input or output space that involves the combination or relationship of multiple input variables could be applied during the search. + +With an accurate surrogate model and a well-designed acquisition function, the candidate point(s) which maximize the acquisition function are most likely to achieve improved experimental outcomes. + +The optimization of acquisition function can be performed by analytical or numerical Monte-Carlo methods, depending on the complexity of acquisition functional form. The _obsidian_ library relies heavily on the _BoTorch_ library for MC-based Bayesian Optimization. + +Please refer to section **Tutorials** for simulation studies that demonstrate the entire APO workflow as well as various visualization and analysis methods. + + diff --git a/_sources/wiki/2_Analysis_and_Visualization.md.txt b/_sources/wiki/2_Analysis_and_Visualization.md.txt new file mode 100644 index 0000000..5ef53b9 --- /dev/null +++ b/_sources/wiki/2_Analysis_and_Visualization.md.txt @@ -0,0 +1,106 @@ +# Additional Analysis and Visualization + + +![APO Workflow](https://github.com/MSDLLCpapers/obsidian/blob/main/docs/_static/APO_workflow.png?raw=true) + +This section introduces some additional components, such as retrospective analysis and visualization methods, that are not essential steps in a Sequential Model-Based Optimization (SMBO) algorithm but are valuable in real-world applications for several reasons. + +* The technical details involved in SMBO algorithms, such as surrogate models and acquisition functions, may seem complicated to users with non-quantitative background. As a result, the entire workflow of suggesting new experimental conditions may appear to be a black box for users, which hampers the adoption of this this powerful process optimization technique. Various performance metrics and model interpretation methods help to bridge this gap by providing users with a better intuitive understanding of the underlying algorithms and revealing the decision-making processes involved. + +* The variable importance analysis and/or model interpretation tools can provide critical insights into the optimization process, aiding in a deeper understanding of the variables that influenced the selection of optimal solution and the relationships between input variables, which could be confirmed with additional experiments or scientific domain experts. + +* Providing prediction uncertainty metrics along with the suggested candidates empowers practitioners to make informed decisions and establish realistic expectations of the surrogate model's predictive performance and potential biases. + + +Please refer to submodule [`campaign`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/campaign) for more details of analysis, +and submodule [`plotting`](https://github.com/MSDLLCpapers/obsidian/tree/main/obsidian/plotting) folder for visualization utilities. + +--- +## Post-hoc Data Analysis + +To simplify the description, assuming the preferred direction of any response variable is maximizing, and there are $N$ data points collected for $J$ experimental outcomes: $\{ (X_i,Y_i) \}_{i=1}^N$. + +If there are multiple experimental outcomes ($J \geq 1$), the $j^{th}$ outcome in vector $Y_i$ is denoted as $Y_i^{j}$. + +### Evaluating the Measured Experimental Conditions + +A direct assessment of input features is assigning a binary indicator (True/False) to identify the best performer $X_{opt}$ or an opimal solution set $\chi_{opt}$. + +* Single-objective optimization: The best performer is the one (or more) inputs that achieve the optimal value of measured experimental outcome. + ```{math} + X_{opt} = \underset{X_i, 1\leq i\leq N}{\text{argmax }} Y(X_i) + ``` + +* Multi-objective optimization: + - In some simple cases when the multiple responses $Y^j (1 \leq j \leq J)$ are correlated, there may exist some best performer $X_{opt}$ for all responses simutaneously: + ```{math} + X_{opt} = \underset{1 \leq j \leq J}{\cap} \underset{X_i, 1\leq i\leq N}{\text{argmax }} Y^j(X_i) + ``` + - In most cases, there is trade-off between multiple responses and they cannot be optimized together. The **Pareto Front** ($PF_Y$) is the set of compromise solution, or nondominated solution, which are better than others in terms that improvement in any response $Y_s$ comes only at the expense of making at least another response $\{ Y_t \}_{t\neq s}$ worse: + \begin{align*} + PF_Y = \{Y_i, i \in [1,N]: \{Y_w, w \in [1,N], w\neq i: Y_w^j > Y_i^j, \forall j \in [1,J] \} = \emptyset \} + \end{align*} + And the optimal solution set in feature space or Pareto optimal designs is the set: + \begin{gather*} + \chi_{opt} = \{X_i, 1\leq i\leq N: Y_i \in PF_Y\} + \end{gather*} + + +After multiple iterations, as the recommened candidates converge towards the best-performing or optimal solution set, multiple data points would exhibit near optimal measurements. +However, due to the presence of experimental errors in measuring $Y$, the aforementioned best performer set may not accurately optimize the unknown function $f(X)$. Furthermore, minor deviations in the design space may not have a significant impact on the outcome values. Consequently, we are interested in expanding the the optimal set to include data points within its close neighborhood. + +For single-objective optimization, the distance between the $i^{th}$ outcome to the best performer $\underset{1\leq i' \leq N}{\max Y_{i'}\}-Y_i$ can be used to rank the data points. +For multi-objective optimization, the evaluation is subjective to user preference. Here are several common options to assign continuous score for each data point: +* Its distance to the pareto front $dist(Y_i, PF_Y)$. Various distrance metrics could be explored. +* Its distance to an **utopian point** $Y_U$, which is the desirable experimental outcome(s) specify by the user and doesn't need to be feasible. +* A weighted average across multiple outcomes $\sum_{1\leq j \leq J} w^j * Y_i^j$ (weights are normalized: $\sum_{1\leq j \leq J} w^j = 1, \forall w_j \geq 0$), where the weights $\{w^j\}_{1\leq j \leq J}$ are proportional to the subjective importance of each outcome. In this retrospective analysis, the weights could be different from the weights or formula used in defining the optimization objective. + + + + +### The Overall Optimization Performance Metrics + +To monitor the progress of SMBO workflow, we need to define a scalar evaluation metric to summarize performance over all the $N$ data points. + +* Single-objective optimization: The optimal value (either max or min, depends on target specification) of measured experimental outcome. + +* Multi-objective optimization: + - Hypervolume: First define a **reference point** $r_Y$ in the outcome space that serves as a baseline for evaluating the quality of measured outcomes, i.e. it defines the minimum acceptable value for each outcome. The reference point is often set as the lower bounds for each response. The hypervolume indicator metric $HV$ is the volume of the space enclosed between the reference point $r_Y$ and the Pareto front $PF_Y$. It access the quality of a pareto front solution set: the larger the better. The space can be computed as the union of hyperrectangles bounded by each point in $PF_Y$ and $r_Y$ as vertices: + \begin{equation*} + HV(\{Y_i\}_{i=1}^N, r_Y) = HV(PF_Y, r_Y) = Volume(\underset{y \in PF_Y}{\cup} [y,r_Y]) + \end{equation*} + - Maximum weighted outcome: The maximum weighted outcomes $\underset{1\leq i \leq N}{\text{max }} \sum_{1\leq j \leq J} w^j * Y_i^j$ + - Minimum distance to the utopian point: $\underset{1\leq i \leq N}{\text{min }} dist(Y_i,Y_U)$ + + +--- +## Surrogate Model Interpretation + +### SHAP (SHapley Additive exPlanations) + +### Partial Dependence Plot + +### Individual Conditional Expectation + +### Sensitivity Analysis + + + + + +--- +## Augmented Predictive Information for Candidates + + + +### Prediction Uncertainty +(TBA...) + + + + + +--- +## Miscellaneous + +(TBA...) diff --git a/_sources/wiki/3_Data.md.txt b/_sources/wiki/3_Data.md.txt new file mode 100644 index 0000000..c8daa1d --- /dev/null +++ b/_sources/wiki/3_Data.md.txt @@ -0,0 +1,3 @@ +# Data + +(TBA...) \ No newline at end of file diff --git a/_sources/wiki/4_SurrogateModel.md.txt b/_sources/wiki/4_SurrogateModel.md.txt new file mode 100644 index 0000000..fec2840 --- /dev/null +++ b/_sources/wiki/4_SurrogateModel.md.txt @@ -0,0 +1,3 @@ +# Surrogate Model + +(TBA...) \ No newline at end of file diff --git a/_sources/wiki/5_AcquisitionFunction.md.txt b/_sources/wiki/5_AcquisitionFunction.md.txt new file mode 100644 index 0000000..9b32ee3 --- /dev/null +++ b/_sources/wiki/5_AcquisitionFunction.md.txt @@ -0,0 +1,3 @@ +# Acquisition Function + +(TBA...) \ No newline at end of file diff --git a/_static/APO_workflow.png b/_static/APO_workflow.png new file mode 100644 index 0000000..cb6c2bf Binary files /dev/null and b/_static/APO_workflow.png differ diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..2af6139 --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 270px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..4d67807 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000..7e4c114 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/language_data.js b/_static/language_data.js new file mode 100644 index 0000000..367b8ed --- /dev/null +++ b/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/_static/minus.png differ diff --git a/_static/mycss.css b/_static/mycss.css new file mode 100644 index 0000000..182d881 --- /dev/null +++ b/_static/mycss.css @@ -0,0 +1,10 @@ + +/* https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/layout.html#horizontal-spacing */ + +.bd-main .bd-content .bd-article-container { + max-width: 100%; /* default is 60em */ +} + +.bd-page-width { + max-width: 100%; /* default is 88rem */ +} \ No newline at end of file diff --git a/_static/obsidian_logo.png b/_static/obsidian_logo.png new file mode 100644 index 0000000..fe6ddcf Binary files /dev/null and b/_static/obsidian_logo.png differ diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/_static/plus.png differ diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 0000000..012e6a0 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,152 @@ +html[data-theme="light"] .highlight pre { line-height: 125%; } +html[data-theme="light"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight .hll { background-color: #fae4c2 } +html[data-theme="light"] .highlight { background: #fefefe; color: #080808 } +html[data-theme="light"] .highlight .c { color: #515151 } /* Comment */ +html[data-theme="light"] .highlight .err { color: #a12236 } /* Error */ +html[data-theme="light"] .highlight .k { color: #6730c5 } /* Keyword */ +html[data-theme="light"] .highlight .l { color: #7f4707 } /* Literal */ +html[data-theme="light"] .highlight .n { color: #080808 } /* Name */ +html[data-theme="light"] .highlight .o { color: #00622f } /* Operator */ +html[data-theme="light"] .highlight .p { color: #080808 } /* Punctuation */ +html[data-theme="light"] .highlight .ch { color: #515151 } /* Comment.Hashbang */ +html[data-theme="light"] .highlight .cm { color: #515151 } /* Comment.Multiline */ +html[data-theme="light"] .highlight .cp { color: #515151 } /* Comment.Preproc */ +html[data-theme="light"] .highlight .cpf { color: #515151 } /* Comment.PreprocFile */ +html[data-theme="light"] .highlight .c1 { color: #515151 } /* Comment.Single */ +html[data-theme="light"] .highlight .cs { color: #515151 } /* Comment.Special */ +html[data-theme="light"] .highlight .gd { color: #005b82 } /* Generic.Deleted */ +html[data-theme="light"] .highlight .ge { font-style: italic } /* Generic.Emph */ +html[data-theme="light"] .highlight .gh { color: #005b82 } /* Generic.Heading */ +html[data-theme="light"] .highlight .gs { font-weight: bold } /* Generic.Strong */ +html[data-theme="light"] .highlight .gu { color: #005b82 } /* Generic.Subheading */ +html[data-theme="light"] .highlight .kc { color: #6730c5 } /* Keyword.Constant */ +html[data-theme="light"] .highlight .kd { color: #6730c5 } /* Keyword.Declaration */ +html[data-theme="light"] .highlight .kn { color: #6730c5 } /* Keyword.Namespace */ +html[data-theme="light"] .highlight .kp { color: #6730c5 } /* Keyword.Pseudo */ +html[data-theme="light"] .highlight .kr { color: #6730c5 } /* Keyword.Reserved */ +html[data-theme="light"] .highlight .kt { color: #7f4707 } /* Keyword.Type */ +html[data-theme="light"] .highlight .ld { color: #7f4707 } /* Literal.Date */ +html[data-theme="light"] .highlight .m { color: #7f4707 } /* Literal.Number */ +html[data-theme="light"] .highlight .s { color: #00622f } /* Literal.String */ +html[data-theme="light"] .highlight .na { color: #912583 } /* Name.Attribute */ +html[data-theme="light"] .highlight .nb { color: #7f4707 } /* Name.Builtin */ +html[data-theme="light"] .highlight .nc { color: #005b82 } /* Name.Class */ +html[data-theme="light"] .highlight .no { color: #005b82 } /* Name.Constant */ +html[data-theme="light"] .highlight .nd { color: #7f4707 } /* Name.Decorator */ +html[data-theme="light"] .highlight .ni { color: #00622f } /* Name.Entity */ +html[data-theme="light"] .highlight .ne { color: #6730c5 } /* Name.Exception */ +html[data-theme="light"] .highlight .nf { color: #005b82 } /* Name.Function */ +html[data-theme="light"] .highlight .nl { color: #7f4707 } /* Name.Label */ +html[data-theme="light"] .highlight .nn { color: #080808 } /* Name.Namespace */ +html[data-theme="light"] .highlight .nx { color: #080808 } /* Name.Other */ +html[data-theme="light"] .highlight .py { color: #005b82 } /* Name.Property */ +html[data-theme="light"] .highlight .nt { color: #005b82 } /* Name.Tag */ +html[data-theme="light"] .highlight .nv { color: #a12236 } /* Name.Variable */ +html[data-theme="light"] .highlight .ow { color: #6730c5 } /* Operator.Word */ +html[data-theme="light"] .highlight .pm { color: #080808 } /* Punctuation.Marker */ +html[data-theme="light"] .highlight .w { color: #080808 } /* Text.Whitespace */ +html[data-theme="light"] .highlight .mb { color: #7f4707 } /* Literal.Number.Bin */ +html[data-theme="light"] .highlight .mf { color: #7f4707 } /* Literal.Number.Float */ +html[data-theme="light"] .highlight .mh { color: #7f4707 } /* Literal.Number.Hex */ +html[data-theme="light"] .highlight .mi { color: #7f4707 } /* Literal.Number.Integer */ +html[data-theme="light"] .highlight .mo { color: #7f4707 } /* Literal.Number.Oct */ +html[data-theme="light"] .highlight .sa { color: #00622f } /* Literal.String.Affix */ +html[data-theme="light"] .highlight .sb { color: #00622f } /* Literal.String.Backtick */ +html[data-theme="light"] .highlight .sc { color: #00622f } /* Literal.String.Char */ +html[data-theme="light"] .highlight .dl { color: #00622f } /* Literal.String.Delimiter */ +html[data-theme="light"] .highlight .sd { color: #00622f } /* Literal.String.Doc */ +html[data-theme="light"] .highlight .s2 { color: #00622f } /* Literal.String.Double */ +html[data-theme="light"] .highlight .se { color: #00622f } /* Literal.String.Escape */ +html[data-theme="light"] .highlight .sh { color: #00622f } /* Literal.String.Heredoc */ +html[data-theme="light"] .highlight .si { color: #00622f } /* Literal.String.Interpol */ +html[data-theme="light"] .highlight .sx { color: #00622f } /* Literal.String.Other */ +html[data-theme="light"] .highlight .sr { color: #a12236 } /* Literal.String.Regex */ +html[data-theme="light"] .highlight .s1 { color: #00622f } /* Literal.String.Single */ +html[data-theme="light"] .highlight .ss { color: #005b82 } /* Literal.String.Symbol */ +html[data-theme="light"] .highlight .bp { color: #7f4707 } /* Name.Builtin.Pseudo */ +html[data-theme="light"] .highlight .fm { color: #005b82 } /* Name.Function.Magic */ +html[data-theme="light"] .highlight .vc { color: #a12236 } /* Name.Variable.Class */ +html[data-theme="light"] .highlight .vg { color: #a12236 } /* Name.Variable.Global */ +html[data-theme="light"] .highlight .vi { color: #a12236 } /* Name.Variable.Instance */ +html[data-theme="light"] .highlight .vm { color: #7f4707 } /* Name.Variable.Magic */ +html[data-theme="light"] .highlight .il { color: #7f4707 } /* Literal.Number.Integer.Long */ +html[data-theme="dark"] .highlight pre { line-height: 125%; } +html[data-theme="dark"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight .hll { background-color: #ffd9002e } +html[data-theme="dark"] .highlight { background: #2b2b2b; color: #f8f8f2 } +html[data-theme="dark"] .highlight .c { color: #ffd900 } /* Comment */ +html[data-theme="dark"] .highlight .err { color: #ffa07a } /* Error */ +html[data-theme="dark"] .highlight .k { color: #dcc6e0 } /* Keyword */ +html[data-theme="dark"] .highlight .l { color: #ffd900 } /* Literal */ +html[data-theme="dark"] .highlight .n { color: #f8f8f2 } /* Name */ +html[data-theme="dark"] .highlight .o { color: #abe338 } /* Operator */ +html[data-theme="dark"] .highlight .p { color: #f8f8f2 } /* Punctuation */ +html[data-theme="dark"] .highlight .ch { color: #ffd900 } /* Comment.Hashbang */ +html[data-theme="dark"] .highlight .cm { color: #ffd900 } /* Comment.Multiline */ +html[data-theme="dark"] .highlight .cp { color: #ffd900 } /* Comment.Preproc */ +html[data-theme="dark"] .highlight .cpf { color: #ffd900 } /* Comment.PreprocFile */ +html[data-theme="dark"] .highlight .c1 { color: #ffd900 } /* Comment.Single */ +html[data-theme="dark"] .highlight .cs { color: #ffd900 } /* Comment.Special */ +html[data-theme="dark"] .highlight .gd { color: #00e0e0 } /* Generic.Deleted */ +html[data-theme="dark"] .highlight .ge { font-style: italic } /* Generic.Emph */ +html[data-theme="dark"] .highlight .gh { color: #00e0e0 } /* Generic.Heading */ +html[data-theme="dark"] .highlight .gs { font-weight: bold } /* Generic.Strong */ +html[data-theme="dark"] .highlight .gu { color: #00e0e0 } /* Generic.Subheading */ +html[data-theme="dark"] .highlight .kc { color: #dcc6e0 } /* Keyword.Constant */ +html[data-theme="dark"] .highlight .kd { color: #dcc6e0 } /* Keyword.Declaration */ +html[data-theme="dark"] .highlight .kn { color: #dcc6e0 } /* Keyword.Namespace */ +html[data-theme="dark"] .highlight .kp { color: #dcc6e0 } /* Keyword.Pseudo */ +html[data-theme="dark"] .highlight .kr { color: #dcc6e0 } /* Keyword.Reserved */ +html[data-theme="dark"] .highlight .kt { color: #ffd900 } /* Keyword.Type */ +html[data-theme="dark"] .highlight .ld { color: #ffd900 } /* Literal.Date */ +html[data-theme="dark"] .highlight .m { color: #ffd900 } /* Literal.Number */ +html[data-theme="dark"] .highlight .s { color: #abe338 } /* Literal.String */ +html[data-theme="dark"] .highlight .na { color: #ffd900 } /* Name.Attribute */ +html[data-theme="dark"] .highlight .nb { color: #ffd900 } /* Name.Builtin */ +html[data-theme="dark"] .highlight .nc { color: #00e0e0 } /* Name.Class */ +html[data-theme="dark"] .highlight .no { color: #00e0e0 } /* Name.Constant */ +html[data-theme="dark"] .highlight .nd { color: #ffd900 } /* Name.Decorator */ +html[data-theme="dark"] .highlight .ni { color: #abe338 } /* Name.Entity */ +html[data-theme="dark"] .highlight .ne { color: #dcc6e0 } /* Name.Exception */ +html[data-theme="dark"] .highlight .nf { color: #00e0e0 } /* Name.Function */ +html[data-theme="dark"] .highlight .nl { color: #ffd900 } /* Name.Label */ +html[data-theme="dark"] .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ +html[data-theme="dark"] .highlight .nx { color: #f8f8f2 } /* Name.Other */ +html[data-theme="dark"] .highlight .py { color: #00e0e0 } /* Name.Property */ +html[data-theme="dark"] .highlight .nt { color: #00e0e0 } /* Name.Tag */ +html[data-theme="dark"] .highlight .nv { color: #ffa07a } /* Name.Variable */ +html[data-theme="dark"] .highlight .ow { color: #dcc6e0 } /* Operator.Word */ +html[data-theme="dark"] .highlight .pm { color: #f8f8f2 } /* Punctuation.Marker */ +html[data-theme="dark"] .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ +html[data-theme="dark"] .highlight .mb { color: #ffd900 } /* Literal.Number.Bin */ +html[data-theme="dark"] .highlight .mf { color: #ffd900 } /* Literal.Number.Float */ +html[data-theme="dark"] .highlight .mh { color: #ffd900 } /* Literal.Number.Hex */ +html[data-theme="dark"] .highlight .mi { color: #ffd900 } /* Literal.Number.Integer */ +html[data-theme="dark"] .highlight .mo { color: #ffd900 } /* Literal.Number.Oct */ +html[data-theme="dark"] .highlight .sa { color: #abe338 } /* Literal.String.Affix */ +html[data-theme="dark"] .highlight .sb { color: #abe338 } /* Literal.String.Backtick */ +html[data-theme="dark"] .highlight .sc { color: #abe338 } /* Literal.String.Char */ +html[data-theme="dark"] .highlight .dl { color: #abe338 } /* Literal.String.Delimiter */ +html[data-theme="dark"] .highlight .sd { color: #abe338 } /* Literal.String.Doc */ +html[data-theme="dark"] .highlight .s2 { color: #abe338 } /* Literal.String.Double */ +html[data-theme="dark"] .highlight .se { color: #abe338 } /* Literal.String.Escape */ +html[data-theme="dark"] .highlight .sh { color: #abe338 } /* Literal.String.Heredoc */ +html[data-theme="dark"] .highlight .si { color: #abe338 } /* Literal.String.Interpol */ +html[data-theme="dark"] .highlight .sx { color: #abe338 } /* Literal.String.Other */ +html[data-theme="dark"] .highlight .sr { color: #ffa07a } /* Literal.String.Regex */ +html[data-theme="dark"] .highlight .s1 { color: #abe338 } /* Literal.String.Single */ +html[data-theme="dark"] .highlight .ss { color: #00e0e0 } /* Literal.String.Symbol */ +html[data-theme="dark"] .highlight .bp { color: #ffd900 } /* Name.Builtin.Pseudo */ +html[data-theme="dark"] .highlight .fm { color: #00e0e0 } /* Name.Function.Magic */ +html[data-theme="dark"] .highlight .vc { color: #ffa07a } /* Name.Variable.Class */ +html[data-theme="dark"] .highlight .vg { color: #ffa07a } /* Name.Variable.Global */ +html[data-theme="dark"] .highlight .vi { color: #ffa07a } /* Name.Variable.Instance */ +html[data-theme="dark"] .highlight .vm { color: #ffd900 } /* Name.Variable.Magic */ +html[data-theme="dark"] .highlight .il { color: #ffd900 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/scripts/bootstrap.js b/_static/scripts/bootstrap.js new file mode 100644 index 0000000..c8178de --- /dev/null +++ b/_static/scripts/bootstrap.js @@ -0,0 +1,3 @@ +/*! For license information please see bootstrap.js.LICENSE.txt */ +(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{afterMain:()=>E,afterRead:()=>v,afterWrite:()=>C,applyStyles:()=>$,arrow:()=>J,auto:()=>a,basePlacements:()=>l,beforeMain:()=>y,beforeRead:()=>_,beforeWrite:()=>A,bottom:()=>s,clippingParents:()=>d,computeStyles:()=>it,createPopper:()=>Dt,createPopperBase:()=>St,createPopperLite:()=>$t,detectOverflow:()=>_t,end:()=>h,eventListeners:()=>st,flip:()=>bt,hide:()=>wt,left:()=>r,main:()=>w,modifierPhases:()=>O,offset:()=>Et,placements:()=>g,popper:()=>f,popperGenerator:()=>Lt,popperOffsets:()=>At,preventOverflow:()=>Tt,read:()=>b,reference:()=>p,right:()=>o,start:()=>c,top:()=>n,variationPlacements:()=>m,viewport:()=>u,write:()=>T});var i={};t.r(i),t.d(i,{Alert:()=>Oe,Button:()=>ke,Carousel:()=>li,Collapse:()=>Ei,Dropdown:()=>Ki,Modal:()=>Ln,Offcanvas:()=>Kn,Popover:()=>bs,ScrollSpy:()=>Ls,Tab:()=>Js,Toast:()=>po,Tooltip:()=>fs});var n="top",s="bottom",o="right",r="left",a="auto",l=[n,s,o,r],c="start",h="end",d="clippingParents",u="viewport",f="popper",p="reference",m=l.reduce((function(t,e){return t.concat([e+"-"+c,e+"-"+h])}),[]),g=[].concat(l,[a]).reduce((function(t,e){return t.concat([e,e+"-"+c,e+"-"+h])}),[]),_="beforeRead",b="read",v="afterRead",y="beforeMain",w="main",E="afterMain",A="beforeWrite",T="write",C="afterWrite",O=[_,b,v,y,w,E,A,T,C];function x(t){return t?(t.nodeName||"").toLowerCase():null}function k(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function L(t){return t instanceof k(t).Element||t instanceof Element}function S(t){return t instanceof k(t).HTMLElement||t instanceof HTMLElement}function D(t){return"undefined"!=typeof ShadowRoot&&(t instanceof k(t).ShadowRoot||t instanceof ShadowRoot)}const $={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];S(s)&&x(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});S(n)&&x(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function I(t){return t.split("-")[0]}var N=Math.max,P=Math.min,M=Math.round;function j(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function F(){return!/^((?!chrome|android).)*safari/i.test(j())}function H(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&S(t)&&(s=t.offsetWidth>0&&M(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&M(n.height)/t.offsetHeight||1);var r=(L(t)?k(t):window).visualViewport,a=!F()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function B(t){var e=H(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function W(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&D(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function z(t){return k(t).getComputedStyle(t)}function R(t){return["table","td","th"].indexOf(x(t))>=0}function q(t){return((L(t)?t.ownerDocument:t.document)||window.document).documentElement}function V(t){return"html"===x(t)?t:t.assignedSlot||t.parentNode||(D(t)?t.host:null)||q(t)}function Y(t){return S(t)&&"fixed"!==z(t).position?t.offsetParent:null}function K(t){for(var e=k(t),i=Y(t);i&&R(i)&&"static"===z(i).position;)i=Y(i);return i&&("html"===x(i)||"body"===x(i)&&"static"===z(i).position)?e:i||function(t){var e=/firefox/i.test(j());if(/Trident/i.test(j())&&S(t)&&"fixed"===z(t).position)return null;var i=V(t);for(D(i)&&(i=i.host);S(i)&&["html","body"].indexOf(x(i))<0;){var n=z(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Q(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function X(t,e,i){return N(t,P(e,i))}function U(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function G(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const J={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,a=t.name,c=t.options,h=i.elements.arrow,d=i.modifiersData.popperOffsets,u=I(i.placement),f=Q(u),p=[r,o].indexOf(u)>=0?"height":"width";if(h&&d){var m=function(t,e){return U("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:G(t,l))}(c.padding,i),g=B(h),_="y"===f?n:r,b="y"===f?s:o,v=i.rects.reference[p]+i.rects.reference[f]-d[f]-i.rects.popper[p],y=d[f]-i.rects.reference[f],w=K(h),E=w?"y"===f?w.clientHeight||0:w.clientWidth||0:0,A=v/2-y/2,T=m[_],C=E-g[p]-m[b],O=E/2-g[p]/2+A,x=X(T,O,C),k=f;i.modifiersData[a]=((e={})[k]=x,e.centerOffset=x-O,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&W(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Z(t){return t.split("-")[1]}var tt={top:"auto",right:"auto",bottom:"auto",left:"auto"};function et(t){var e,i=t.popper,a=t.popperRect,l=t.placement,c=t.variation,d=t.offsets,u=t.position,f=t.gpuAcceleration,p=t.adaptive,m=t.roundOffsets,g=t.isFixed,_=d.x,b=void 0===_?0:_,v=d.y,y=void 0===v?0:v,w="function"==typeof m?m({x:b,y}):{x:b,y};b=w.x,y=w.y;var E=d.hasOwnProperty("x"),A=d.hasOwnProperty("y"),T=r,C=n,O=window;if(p){var x=K(i),L="clientHeight",S="clientWidth";x===k(i)&&"static"!==z(x=q(i)).position&&"absolute"===u&&(L="scrollHeight",S="scrollWidth"),(l===n||(l===r||l===o)&&c===h)&&(C=s,y-=(g&&x===O&&O.visualViewport?O.visualViewport.height:x[L])-a.height,y*=f?1:-1),l!==r&&(l!==n&&l!==s||c!==h)||(T=o,b-=(g&&x===O&&O.visualViewport?O.visualViewport.width:x[S])-a.width,b*=f?1:-1)}var D,$=Object.assign({position:u},p&&tt),I=!0===m?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:M(i*s)/s||0,y:M(n*s)/s||0}}({x:b,y},k(i)):{x:b,y};return b=I.x,y=I.y,f?Object.assign({},$,((D={})[C]=A?"0":"",D[T]=E?"0":"",D.transform=(O.devicePixelRatio||1)<=1?"translate("+b+"px, "+y+"px)":"translate3d("+b+"px, "+y+"px, 0)",D)):Object.assign({},$,((e={})[C]=A?y+"px":"",e[T]=E?b+"px":"",e.transform="",e))}const it={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:I(e.placement),variation:Z(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,et(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,et(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var nt={passive:!0};const st={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=k(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,nt)})),a&&l.addEventListener("resize",i.update,nt),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,nt)})),a&&l.removeEventListener("resize",i.update,nt)}},data:{}};var ot={left:"right",right:"left",bottom:"top",top:"bottom"};function rt(t){return t.replace(/left|right|bottom|top/g,(function(t){return ot[t]}))}var at={start:"end",end:"start"};function lt(t){return t.replace(/start|end/g,(function(t){return at[t]}))}function ct(t){var e=k(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ht(t){return H(q(t)).left+ct(t).scrollLeft}function dt(t){var e=z(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function ut(t){return["html","body","#document"].indexOf(x(t))>=0?t.ownerDocument.body:S(t)&&dt(t)?t:ut(V(t))}function ft(t,e){var i;void 0===e&&(e=[]);var n=ut(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=k(n),r=s?[o].concat(o.visualViewport||[],dt(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(ft(V(r)))}function pt(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function mt(t,e,i){return e===u?pt(function(t,e){var i=k(t),n=q(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=F();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+ht(t),y:l}}(t,i)):L(e)?function(t,e){var i=H(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):pt(function(t){var e,i=q(t),n=ct(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=N(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=N(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ht(t),l=-n.scrollTop;return"rtl"===z(s||i).direction&&(a+=N(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(q(t)))}function gt(t){var e,i=t.reference,a=t.element,l=t.placement,d=l?I(l):null,u=l?Z(l):null,f=i.x+i.width/2-a.width/2,p=i.y+i.height/2-a.height/2;switch(d){case n:e={x:f,y:i.y-a.height};break;case s:e={x:f,y:i.y+i.height};break;case o:e={x:i.x+i.width,y:p};break;case r:e={x:i.x-a.width,y:p};break;default:e={x:i.x,y:i.y}}var m=d?Q(d):null;if(null!=m){var g="y"===m?"height":"width";switch(u){case c:e[m]=e[m]-(i[g]/2-a[g]/2);break;case h:e[m]=e[m]+(i[g]/2-a[g]/2)}}return e}function _t(t,e){void 0===e&&(e={});var i=e,r=i.placement,a=void 0===r?t.placement:r,c=i.strategy,h=void 0===c?t.strategy:c,m=i.boundary,g=void 0===m?d:m,_=i.rootBoundary,b=void 0===_?u:_,v=i.elementContext,y=void 0===v?f:v,w=i.altBoundary,E=void 0!==w&&w,A=i.padding,T=void 0===A?0:A,C=U("number"!=typeof T?T:G(T,l)),O=y===f?p:f,k=t.rects.popper,D=t.elements[E?O:y],$=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=ft(V(t)),i=["absolute","fixed"].indexOf(z(t).position)>=0&&S(t)?K(t):t;return L(i)?e.filter((function(t){return L(t)&&W(t,i)&&"body"!==x(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=mt(t,i,n);return e.top=N(s.top,e.top),e.right=P(s.right,e.right),e.bottom=P(s.bottom,e.bottom),e.left=N(s.left,e.left),e}),mt(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(L(D)?D:D.contextElement||q(t.elements.popper),g,b,h),I=H(t.elements.reference),M=gt({reference:I,element:k,strategy:"absolute",placement:a}),j=pt(Object.assign({},k,M)),F=y===f?j:I,B={top:$.top-F.top+C.top,bottom:F.bottom-$.bottom+C.bottom,left:$.left-F.left+C.left,right:F.right-$.right+C.right},R=t.modifiersData.offset;if(y===f&&R){var Y=R[a];Object.keys(B).forEach((function(t){var e=[o,s].indexOf(t)>=0?1:-1,i=[n,s].indexOf(t)>=0?"y":"x";B[t]+=Y[i]*e}))}return B}const bt={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,h=t.name;if(!e.modifiersData[h]._skip){for(var d=i.mainAxis,u=void 0===d||d,f=i.altAxis,p=void 0===f||f,_=i.fallbackPlacements,b=i.padding,v=i.boundary,y=i.rootBoundary,w=i.altBoundary,E=i.flipVariations,A=void 0===E||E,T=i.allowedAutoPlacements,C=e.options.placement,O=I(C),x=_||(O!==C&&A?function(t){if(I(t)===a)return[];var e=rt(t);return[lt(t),e,lt(e)]}(C):[rt(C)]),k=[C].concat(x).reduce((function(t,i){return t.concat(I(i)===a?function(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,c=i.allowedAutoPlacements,h=void 0===c?g:c,d=Z(n),u=d?a?m:m.filter((function(t){return Z(t)===d})):l,f=u.filter((function(t){return h.indexOf(t)>=0}));0===f.length&&(f=u);var p=f.reduce((function(e,i){return e[i]=_t(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[I(i)],e}),{});return Object.keys(p).sort((function(t,e){return p[t]-p[e]}))}(e,{placement:i,boundary:v,rootBoundary:y,padding:b,flipVariations:A,allowedAutoPlacements:T}):i)}),[]),L=e.rects.reference,S=e.rects.popper,D=new Map,$=!0,N=k[0],P=0;P=0,B=H?"width":"height",W=_t(e,{placement:M,boundary:v,rootBoundary:y,altBoundary:w,padding:b}),z=H?F?o:r:F?s:n;L[B]>S[B]&&(z=rt(z));var R=rt(z),q=[];if(u&&q.push(W[j]<=0),p&&q.push(W[z]<=0,W[R]<=0),q.every((function(t){return t}))){N=M,$=!1;break}D.set(M,q)}if($)for(var V=function(t){var e=k.find((function(e){var i=D.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return N=e,"break"},Y=A?3:1;Y>0&&"break"!==V(Y);Y--);e.placement!==N&&(e.modifiersData[h]._skip=!0,e.placement=N,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function vt(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function yt(t){return[n,o,s,r].some((function(e){return t[e]>=0}))}const wt={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=_t(e,{elementContext:"reference"}),a=_t(e,{altBoundary:!0}),l=vt(r,n),c=vt(a,s,o),h=yt(l),d=yt(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Et={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,s=t.name,a=i.offset,l=void 0===a?[0,0]:a,c=g.reduce((function(t,i){return t[i]=function(t,e,i){var s=I(t),a=[r,n].indexOf(s)>=0?-1:1,l="function"==typeof i?i(Object.assign({},e,{placement:t})):i,c=l[0],h=l[1];return c=c||0,h=(h||0)*a,[r,o].indexOf(s)>=0?{x:h,y:c}:{x:c,y:h}}(i,e.rects,l),t}),{}),h=c[e.placement],d=h.x,u=h.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=d,e.modifiersData.popperOffsets.y+=u),e.modifiersData[s]=c}},At={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=gt({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},Tt={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,a=t.name,l=i.mainAxis,h=void 0===l||l,d=i.altAxis,u=void 0!==d&&d,f=i.boundary,p=i.rootBoundary,m=i.altBoundary,g=i.padding,_=i.tether,b=void 0===_||_,v=i.tetherOffset,y=void 0===v?0:v,w=_t(e,{boundary:f,rootBoundary:p,padding:g,altBoundary:m}),E=I(e.placement),A=Z(e.placement),T=!A,C=Q(E),O="x"===C?"y":"x",x=e.modifiersData.popperOffsets,k=e.rects.reference,L=e.rects.popper,S="function"==typeof y?y(Object.assign({},e.rects,{placement:e.placement})):y,D="number"==typeof S?{mainAxis:S,altAxis:S}:Object.assign({mainAxis:0,altAxis:0},S),$=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,M={x:0,y:0};if(x){if(h){var j,F="y"===C?n:r,H="y"===C?s:o,W="y"===C?"height":"width",z=x[C],R=z+w[F],q=z-w[H],V=b?-L[W]/2:0,Y=A===c?k[W]:L[W],U=A===c?-L[W]:-k[W],G=e.elements.arrow,J=b&&G?B(G):{width:0,height:0},tt=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},et=tt[F],it=tt[H],nt=X(0,k[W],J[W]),st=T?k[W]/2-V-nt-et-D.mainAxis:Y-nt-et-D.mainAxis,ot=T?-k[W]/2+V+nt+it+D.mainAxis:U+nt+it+D.mainAxis,rt=e.elements.arrow&&K(e.elements.arrow),at=rt?"y"===C?rt.clientTop||0:rt.clientLeft||0:0,lt=null!=(j=null==$?void 0:$[C])?j:0,ct=z+ot-lt,ht=X(b?P(R,z+st-lt-at):R,z,b?N(q,ct):q);x[C]=ht,M[C]=ht-z}if(u){var dt,ut="x"===C?n:r,ft="x"===C?s:o,pt=x[O],mt="y"===O?"height":"width",gt=pt+w[ut],bt=pt-w[ft],vt=-1!==[n,r].indexOf(E),yt=null!=(dt=null==$?void 0:$[O])?dt:0,wt=vt?gt:pt-k[mt]-L[mt]-yt+D.altAxis,Et=vt?pt+k[mt]+L[mt]-yt-D.altAxis:bt,At=b&&vt?function(t,e,i){var n=X(t,e,i);return n>i?i:n}(wt,pt,Et):X(b?wt:gt,pt,b?Et:bt);x[O]=At,M[O]=At-pt}e.modifiersData[a]=M}},requiresIfExists:["offset"]};function Ct(t,e,i){void 0===i&&(i=!1);var n,s,o=S(e),r=S(e)&&function(t){var e=t.getBoundingClientRect(),i=M(e.width)/t.offsetWidth||1,n=M(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=q(e),l=H(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==x(e)||dt(a))&&(c=(n=e)!==k(n)&&S(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:ct(n)),S(e)?((h=H(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=ht(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function Ot(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var xt={placement:"bottom",modifiers:[],strategy:"absolute"};function kt(){for(var t=arguments.length,e=new Array(t),i=0;iIt.has(t)&&It.get(t).get(e)||null,remove(t,e){if(!It.has(t))return;const i=It.get(t);i.delete(e),0===i.size&&It.delete(t)}},Pt="transitionend",Mt=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),jt=t=>{t.dispatchEvent(new Event(Pt))},Ft=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),Ht=t=>Ft(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(Mt(t)):null,Bt=t=>{if(!Ft(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},Wt=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),zt=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?zt(t.parentNode):null},Rt=()=>{},qt=t=>{t.offsetHeight},Vt=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,Yt=[],Kt=()=>"rtl"===document.documentElement.dir,Qt=t=>{var e;e=()=>{const e=Vt();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(Yt.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of Yt)t()})),Yt.push(e)):e()},Xt=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,Ut=(t,e,i=!0)=>{if(!i)return void Xt(t);const n=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let s=!1;const o=({target:i})=>{i===e&&(s=!0,e.removeEventListener(Pt,o),Xt(t))};e.addEventListener(Pt,o),setTimeout((()=>{s||jt(e)}),n)},Gt=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},Jt=/[^.]*(?=\..*)\.|.*/,Zt=/\..*/,te=/::\d+$/,ee={};let ie=1;const ne={mouseenter:"mouseover",mouseleave:"mouseout"},se=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function oe(t,e){return e&&`${e}::${ie++}`||t.uidEvent||ie++}function re(t){const e=oe(t);return t.uidEvent=e,ee[e]=ee[e]||{},ee[e]}function ae(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function le(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=ue(t);return se.has(o)||(o=t),[n,s,o]}function ce(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=le(e,i,n);if(e in ne){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=re(t),c=l[a]||(l[a]={}),h=ae(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=oe(r,e.replace(Jt,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return pe(s,{delegateTarget:r}),n.oneOff&&fe.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return pe(n,{delegateTarget:t}),i.oneOff&&fe.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function he(t,e,i,n,s){const o=ae(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function de(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&he(t,e,i,r.callable,r.delegationSelector)}function ue(t){return t=t.replace(Zt,""),ne[t]||t}const fe={on(t,e,i,n){ce(t,e,i,n,!1)},one(t,e,i,n){ce(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=le(e,i,n),a=r!==e,l=re(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))de(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(te,"");a&&!e.includes(s)||he(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;he(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=Vt();let s=null,o=!0,r=!0,a=!1;e!==ue(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=pe(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function pe(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function me(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function ge(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const _e={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${ge(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${ge(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=me(t.dataset[n])}return e},getDataAttribute:(t,e)=>me(t.getAttribute(`data-bs-${ge(e)}`))};class be{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=Ft(e)?_e.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...Ft(e)?_e.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],o=Ft(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(o))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${o}" but expected type "${s}".`)}var i}}class ve extends be{constructor(t,e){super(),(t=Ht(t))&&(this._element=t,this._config=this._getConfig(e),Nt.set(this._element,this.constructor.DATA_KEY,this))}dispose(){Nt.remove(this._element,this.constructor.DATA_KEY),fe.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){Ut(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return Nt.get(Ht(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const ye=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>Mt(t))).join(","):null},we={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!Wt(t)&&Bt(t)))},getSelectorFromElement(t){const e=ye(t);return e&&we.findOne(e)?e:null},getElementFromSelector(t){const e=ye(t);return e?we.findOne(e):null},getMultipleElementsFromSelector(t){const e=ye(t);return e?we.find(e):[]}},Ee=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;fe.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),Wt(this))return;const s=we.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},Ae=".bs.alert",Te=`close${Ae}`,Ce=`closed${Ae}`;class Oe extends ve{static get NAME(){return"alert"}close(){if(fe.trigger(this._element,Te).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),fe.trigger(this._element,Ce),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Oe.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}Ee(Oe,"close"),Qt(Oe);const xe='[data-bs-toggle="button"]';class ke extends ve{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=ke.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}fe.on(document,"click.bs.button.data-api",xe,(t=>{t.preventDefault();const e=t.target.closest(xe);ke.getOrCreateInstance(e).toggle()})),Qt(ke);const Le=".bs.swipe",Se=`touchstart${Le}`,De=`touchmove${Le}`,$e=`touchend${Le}`,Ie=`pointerdown${Le}`,Ne=`pointerup${Le}`,Pe={endCallback:null,leftCallback:null,rightCallback:null},Me={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class je extends be{constructor(t,e){super(),this._element=t,t&&je.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return Pe}static get DefaultType(){return Me}static get NAME(){return"swipe"}dispose(){fe.off(this._element,Le)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),Xt(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&Xt(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(fe.on(this._element,Ie,(t=>this._start(t))),fe.on(this._element,Ne,(t=>this._end(t))),this._element.classList.add("pointer-event")):(fe.on(this._element,Se,(t=>this._start(t))),fe.on(this._element,De,(t=>this._move(t))),fe.on(this._element,$e,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const Fe=".bs.carousel",He=".data-api",Be="ArrowLeft",We="ArrowRight",ze="next",Re="prev",qe="left",Ve="right",Ye=`slide${Fe}`,Ke=`slid${Fe}`,Qe=`keydown${Fe}`,Xe=`mouseenter${Fe}`,Ue=`mouseleave${Fe}`,Ge=`dragstart${Fe}`,Je=`load${Fe}${He}`,Ze=`click${Fe}${He}`,ti="carousel",ei="active",ii=".active",ni=".carousel-item",si=ii+ni,oi={[Be]:Ve,[We]:qe},ri={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},ai={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class li extends ve{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=we.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===ti&&this.cycle()}static get Default(){return ri}static get DefaultType(){return ai}static get NAME(){return"carousel"}next(){this._slide(ze)}nextWhenVisible(){!document.hidden&&Bt(this._element)&&this.next()}prev(){this._slide(Re)}pause(){this._isSliding&&jt(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?fe.one(this._element,Ke,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void fe.one(this._element,Ke,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?ze:Re;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&fe.on(this._element,Qe,(t=>this._keydown(t))),"hover"===this._config.pause&&(fe.on(this._element,Xe,(()=>this.pause())),fe.on(this._element,Ue,(()=>this._maybeEnableCycle()))),this._config.touch&&je.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of we.find(".carousel-item img",this._element))fe.on(t,Ge,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(qe)),rightCallback:()=>this._slide(this._directionToOrder(Ve)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new je(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=oi[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=we.findOne(ii,this._indicatorsElement);e.classList.remove(ei),e.removeAttribute("aria-current");const i=we.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(ei),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===ze,s=e||Gt(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>fe.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(Ye).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),qt(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(ei),i.classList.remove(ei,c,l),this._isSliding=!1,r(Ke)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return we.findOne(si,this._element)}_getItems(){return we.find(ni,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return Kt()?t===qe?Re:ze:t===qe?ze:Re}_orderToDirection(t){return Kt()?t===Re?qe:Ve:t===Re?Ve:qe}static jQueryInterface(t){return this.each((function(){const e=li.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}fe.on(document,Ze,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=we.getElementFromSelector(this);if(!e||!e.classList.contains(ti))return;t.preventDefault();const i=li.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===_e.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),fe.on(window,Je,(()=>{const t=we.find('[data-bs-ride="carousel"]');for(const e of t)li.getOrCreateInstance(e)})),Qt(li);const ci=".bs.collapse",hi=`show${ci}`,di=`shown${ci}`,ui=`hide${ci}`,fi=`hidden${ci}`,pi=`click${ci}.data-api`,mi="show",gi="collapse",_i="collapsing",bi=`:scope .${gi} .${gi}`,vi='[data-bs-toggle="collapse"]',yi={parent:null,toggle:!0},wi={parent:"(null|element)",toggle:"boolean"};class Ei extends ve{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=we.find(vi);for(const t of i){const e=we.getSelectorFromElement(t),i=we.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return yi}static get DefaultType(){return wi}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Ei.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(fe.trigger(this._element,hi).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(gi),this._element.classList.add(_i),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(_i),this._element.classList.add(gi,mi),this._element.style[e]="",fe.trigger(this._element,di)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(fe.trigger(this._element,ui).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,qt(this._element),this._element.classList.add(_i),this._element.classList.remove(gi,mi);for(const t of this._triggerArray){const e=we.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(_i),this._element.classList.add(gi),fe.trigger(this._element,fi)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(mi)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=Ht(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(vi);for(const e of t){const t=we.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=we.find(bi,this._config.parent);return we.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Ei.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}fe.on(document,pi,vi,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of we.getMultipleElementsFromSelector(this))Ei.getOrCreateInstance(t,{toggle:!1}).toggle()})),Qt(Ei);const Ai="dropdown",Ti=".bs.dropdown",Ci=".data-api",Oi="ArrowUp",xi="ArrowDown",ki=`hide${Ti}`,Li=`hidden${Ti}`,Si=`show${Ti}`,Di=`shown${Ti}`,$i=`click${Ti}${Ci}`,Ii=`keydown${Ti}${Ci}`,Ni=`keyup${Ti}${Ci}`,Pi="show",Mi='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',ji=`${Mi}.${Pi}`,Fi=".dropdown-menu",Hi=Kt()?"top-end":"top-start",Bi=Kt()?"top-start":"top-end",Wi=Kt()?"bottom-end":"bottom-start",zi=Kt()?"bottom-start":"bottom-end",Ri=Kt()?"left-start":"right-start",qi=Kt()?"right-start":"left-start",Vi={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Yi={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class Ki extends ve{constructor(t,e){super(t,e),this._popper=null,this._parent=this._element.parentNode,this._menu=we.next(this._element,Fi)[0]||we.prev(this._element,Fi)[0]||we.findOne(Fi,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return Vi}static get DefaultType(){return Yi}static get NAME(){return Ai}toggle(){return this._isShown()?this.hide():this.show()}show(){if(Wt(this._element)||this._isShown())return;const t={relatedTarget:this._element};if(!fe.trigger(this._element,Si,t).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const t of[].concat(...document.body.children))fe.on(t,"mouseover",Rt);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Pi),this._element.classList.add(Pi),fe.trigger(this._element,Di,t)}}hide(){if(Wt(this._element)||!this._isShown())return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){if(!fe.trigger(this._element,ki,t).defaultPrevented){if("ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.off(t,"mouseover",Rt);this._popper&&this._popper.destroy(),this._menu.classList.remove(Pi),this._element.classList.remove(Pi),this._element.setAttribute("aria-expanded","false"),_e.removeDataAttribute(this._menu,"popper"),fe.trigger(this._element,Li,t)}}_getConfig(t){if("object"==typeof(t=super._getConfig(t)).reference&&!Ft(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Ai.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(){if(void 0===e)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let t=this._element;"parent"===this._config.reference?t=this._parent:Ft(this._config.reference)?t=Ht(this._config.reference):"object"==typeof this._config.reference&&(t=this._config.reference);const i=this._getPopperConfig();this._popper=Dt(t,this._menu,i)}_isShown(){return this._menu.classList.contains(Pi)}_getPlacement(){const t=this._parent;if(t.classList.contains("dropend"))return Ri;if(t.classList.contains("dropstart"))return qi;if(t.classList.contains("dropup-center"))return"top";if(t.classList.contains("dropdown-center"))return"bottom";const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?Bi:Hi:e?zi:Wi}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(_e.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...Xt(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=we.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>Bt(t)));i.length&&Gt(i,e,t===xi,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=Ki.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=we.find(ji);for(const i of e){const e=Ki.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Oi,xi].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Mi)?this:we.prev(this,Mi)[0]||we.next(this,Mi)[0]||we.findOne(Mi,t.delegateTarget.parentNode),o=Ki.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}fe.on(document,Ii,Mi,Ki.dataApiKeydownHandler),fe.on(document,Ii,Fi,Ki.dataApiKeydownHandler),fe.on(document,$i,Ki.clearMenus),fe.on(document,Ni,Ki.clearMenus),fe.on(document,$i,Mi,(function(t){t.preventDefault(),Ki.getOrCreateInstance(this).toggle()})),Qt(Ki);const Qi="backdrop",Xi="show",Ui=`mousedown.bs.${Qi}`,Gi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Ji={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Zi extends be{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Gi}static get DefaultType(){return Ji}static get NAME(){return Qi}show(t){if(!this._config.isVisible)return void Xt(t);this._append();const e=this._getElement();this._config.isAnimated&&qt(e),e.classList.add(Xi),this._emulateAnimation((()=>{Xt(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Xi),this._emulateAnimation((()=>{this.dispose(),Xt(t)}))):Xt(t)}dispose(){this._isAppended&&(fe.off(this._element,Ui),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=Ht(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),fe.on(t,Ui,(()=>{Xt(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){Ut(t,this._getElement(),this._config.isAnimated)}}const tn=".bs.focustrap",en=`focusin${tn}`,nn=`keydown.tab${tn}`,sn="backward",on={autofocus:!0,trapElement:null},rn={autofocus:"boolean",trapElement:"element"};class an extends be{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return on}static get DefaultType(){return rn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),fe.off(document,tn),fe.on(document,en,(t=>this._handleFocusin(t))),fe.on(document,nn,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,fe.off(document,tn))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=we.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===sn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?sn:"forward")}}const ln=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",cn=".sticky-top",hn="padding-right",dn="margin-right";class un{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,hn,(e=>e+t)),this._setElementAttributes(ln,hn,(e=>e+t)),this._setElementAttributes(cn,dn,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,hn),this._resetElementAttributes(ln,hn),this._resetElementAttributes(cn,dn)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&_e.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=_e.getDataAttribute(t,e);null!==i?(_e.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(Ft(t))e(t);else for(const i of we.find(t,this._element))e(i)}}const fn=".bs.modal",pn=`hide${fn}`,mn=`hidePrevented${fn}`,gn=`hidden${fn}`,_n=`show${fn}`,bn=`shown${fn}`,vn=`resize${fn}`,yn=`click.dismiss${fn}`,wn=`mousedown.dismiss${fn}`,En=`keydown.dismiss${fn}`,An=`click${fn}.data-api`,Tn="modal-open",Cn="show",On="modal-static",xn={backdrop:!0,focus:!0,keyboard:!0},kn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class Ln extends ve{constructor(t,e){super(t,e),this._dialog=we.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new un,this._addEventListeners()}static get Default(){return xn}static get DefaultType(){return kn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||fe.trigger(this._element,_n,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Tn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(fe.trigger(this._element,pn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Cn),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){fe.off(window,fn),fe.off(this._dialog,fn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Zi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new an({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=we.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),qt(this._element),this._element.classList.add(Cn),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,fe.trigger(this._element,bn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){fe.on(this._element,En,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),fe.on(window,vn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),fe.on(this._element,wn,(t=>{fe.one(this._element,yn,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(Tn),this._resetAdjustments(),this._scrollBar.reset(),fe.trigger(this._element,gn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(fe.trigger(this._element,mn).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(On)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(On),this._queueCallback((()=>{this._element.classList.remove(On),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=Kt()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=Kt()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Ln.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}fe.on(document,An,'[data-bs-toggle="modal"]',(function(t){const e=we.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),fe.one(e,_n,(t=>{t.defaultPrevented||fe.one(e,gn,(()=>{Bt(this)&&this.focus()}))}));const i=we.findOne(".modal.show");i&&Ln.getInstance(i).hide(),Ln.getOrCreateInstance(e).toggle(this)})),Ee(Ln),Qt(Ln);const Sn=".bs.offcanvas",Dn=".data-api",$n=`load${Sn}${Dn}`,In="show",Nn="showing",Pn="hiding",Mn=".offcanvas.show",jn=`show${Sn}`,Fn=`shown${Sn}`,Hn=`hide${Sn}`,Bn=`hidePrevented${Sn}`,Wn=`hidden${Sn}`,zn=`resize${Sn}`,Rn=`click${Sn}${Dn}`,qn=`keydown.dismiss${Sn}`,Vn={backdrop:!0,keyboard:!0,scroll:!1},Yn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Kn extends ve{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Vn}static get DefaultType(){return Yn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||fe.trigger(this._element,jn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new un).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Nn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(In),this._element.classList.remove(Nn),fe.trigger(this._element,Fn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(fe.trigger(this._element,Hn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(Pn),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(In,Pn),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new un).reset(),fe.trigger(this._element,Wn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Zi({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():fe.trigger(this._element,Bn)}:null})}_initializeFocusTrap(){return new an({trapElement:this._element})}_addEventListeners(){fe.on(this._element,qn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():fe.trigger(this._element,Bn))}))}static jQueryInterface(t){return this.each((function(){const e=Kn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}fe.on(document,Rn,'[data-bs-toggle="offcanvas"]',(function(t){const e=we.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),Wt(this))return;fe.one(e,Wn,(()=>{Bt(this)&&this.focus()}));const i=we.findOne(Mn);i&&i!==e&&Kn.getInstance(i).hide(),Kn.getOrCreateInstance(e).toggle(this)})),fe.on(window,$n,(()=>{for(const t of we.find(Mn))Kn.getOrCreateInstance(t).show()})),fe.on(window,zn,(()=>{for(const t of we.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&Kn.getOrCreateInstance(t).hide()})),Ee(Kn),Qt(Kn);const Qn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Xn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Un=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Gn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Xn.has(i)||Boolean(Un.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Jn={allowList:Qn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Zn={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},ts={entry:"(string|element|function|null)",selector:"(string|element)"};class es extends be{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Jn}static get DefaultType(){return Zn}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},ts)}_setContent(t,e,i){const n=we.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?Ft(e)?this._putElementInTemplate(Ht(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Gn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return Xt(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const is=new Set(["sanitize","allowList","sanitizeFn"]),ns="fade",ss="show",os=".tooltip-inner",rs=".modal",as="hide.bs.modal",ls="hover",cs="focus",hs={AUTO:"auto",TOP:"top",RIGHT:Kt()?"left":"right",BOTTOM:"bottom",LEFT:Kt()?"right":"left"},ds={allowList:Qn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},us={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class fs extends ve{constructor(t,i){if(void 0===e)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,i),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return ds}static get DefaultType(){return us}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),fe.off(this._element.closest(rs),as,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=fe.trigger(this._element,this.constructor.eventName("show")),e=(zt(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),fe.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.on(t,"mouseover",Rt);this._queueCallback((()=>{fe.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!fe.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.off(t,"mouseover",Rt);this._activeTrigger.click=!1,this._activeTrigger[cs]=!1,this._activeTrigger[ls]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),fe.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ns,ss),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ns),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new es({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[os]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ns)}_isShown(){return this.tip&&this.tip.classList.contains(ss)}_createPopper(t){const e=Xt(this._config.placement,[this,t,this._element]),i=hs[e.toUpperCase()];return Dt(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return Xt(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...Xt(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)fe.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ls?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ls?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");fe.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?cs:ls]=!0,e._enter()})),fe.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?cs:ls]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},fe.on(this._element.closest(rs),as,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=_e.getDataAttributes(this._element);for(const t of Object.keys(e))is.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:Ht(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=fs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Qt(fs);const ps=".popover-header",ms=".popover-body",gs={...fs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},_s={...fs.DefaultType,content:"(null|string|element|function)"};class bs extends fs{static get Default(){return gs}static get DefaultType(){return _s}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[ps]:this._getTitle(),[ms]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=bs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Qt(bs);const vs=".bs.scrollspy",ys=`activate${vs}`,ws=`click${vs}`,Es=`load${vs}.data-api`,As="active",Ts="[href]",Cs=".nav-link",Os=`${Cs}, .nav-item > ${Cs}, .list-group-item`,xs={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},ks={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Ls extends ve{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return xs}static get DefaultType(){return ks}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=Ht(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(fe.off(this._config.target,ws),fe.on(this._config.target,ws,Ts,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=we.find(Ts,this._config.target);for(const e of t){if(!e.hash||Wt(e))continue;const t=we.findOne(decodeURI(e.hash),this._element);Bt(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(As),this._activateParents(t),fe.trigger(this._element,ys,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))we.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(As);else for(const e of we.parents(t,".nav, .list-group"))for(const t of we.prev(e,Os))t.classList.add(As)}_clearActiveClass(t){t.classList.remove(As);const e=we.find(`${Ts}.${As}`,t);for(const t of e)t.classList.remove(As)}static jQueryInterface(t){return this.each((function(){const e=Ls.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}fe.on(window,Es,(()=>{for(const t of we.find('[data-bs-spy="scroll"]'))Ls.getOrCreateInstance(t)})),Qt(Ls);const Ss=".bs.tab",Ds=`hide${Ss}`,$s=`hidden${Ss}`,Is=`show${Ss}`,Ns=`shown${Ss}`,Ps=`click${Ss}`,Ms=`keydown${Ss}`,js=`load${Ss}`,Fs="ArrowLeft",Hs="ArrowRight",Bs="ArrowUp",Ws="ArrowDown",zs="Home",Rs="End",qs="active",Vs="fade",Ys="show",Ks=".dropdown-toggle",Qs=`:not(${Ks})`,Xs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Us=`.nav-link${Qs}, .list-group-item${Qs}, [role="tab"]${Qs}, ${Xs}`,Gs=`.${qs}[data-bs-toggle="tab"], .${qs}[data-bs-toggle="pill"], .${qs}[data-bs-toggle="list"]`;class Js extends ve{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),fe.on(this._element,Ms,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?fe.trigger(e,Ds,{relatedTarget:t}):null;fe.trigger(t,Is,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(qs),this._activate(we.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),fe.trigger(t,Ns,{relatedTarget:e})):t.classList.add(Ys)}),t,t.classList.contains(Vs)))}_deactivate(t,e){t&&(t.classList.remove(qs),t.blur(),this._deactivate(we.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),fe.trigger(t,$s,{relatedTarget:e})):t.classList.remove(Ys)}),t,t.classList.contains(Vs)))}_keydown(t){if(![Fs,Hs,Bs,Ws,zs,Rs].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!Wt(t)));let i;if([zs,Rs].includes(t.key))i=e[t.key===zs?0:e.length-1];else{const n=[Hs,Ws].includes(t.key);i=Gt(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Js.getOrCreateInstance(i).show())}_getChildren(){return we.find(Us,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=we.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=we.findOne(t,i);s&&s.classList.toggle(n,e)};n(Ks,qs),n(".dropdown-menu",Ys),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(qs)}_getInnerElement(t){return t.matches(Us)?t:we.findOne(Us,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Js.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}fe.on(document,Ps,Xs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),Wt(this)||Js.getOrCreateInstance(this).show()})),fe.on(window,js,(()=>{for(const t of we.find(Gs))Js.getOrCreateInstance(t)})),Qt(Js);const Zs=".bs.toast",to=`mouseover${Zs}`,eo=`mouseout${Zs}`,io=`focusin${Zs}`,no=`focusout${Zs}`,so=`hide${Zs}`,oo=`hidden${Zs}`,ro=`show${Zs}`,ao=`shown${Zs}`,lo="hide",co="show",ho="showing",uo={animation:"boolean",autohide:"boolean",delay:"number"},fo={animation:!0,autohide:!0,delay:5e3};class po extends ve{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return fo}static get DefaultType(){return uo}static get NAME(){return"toast"}show(){fe.trigger(this._element,ro).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(lo),qt(this._element),this._element.classList.add(co,ho),this._queueCallback((()=>{this._element.classList.remove(ho),fe.trigger(this._element,ao),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(fe.trigger(this._element,so).defaultPrevented||(this._element.classList.add(ho),this._queueCallback((()=>{this._element.classList.add(lo),this._element.classList.remove(ho,co),fe.trigger(this._element,oo)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(co),super.dispose()}isShown(){return this._element.classList.contains(co)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){fe.on(this._element,to,(t=>this._onInteraction(t,!0))),fe.on(this._element,eo,(t=>this._onInteraction(t,!1))),fe.on(this._element,io,(t=>this._onInteraction(t,!0))),fe.on(this._element,no,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=po.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}function mo(t){"loading"!=document.readyState?t():document.addEventListener("DOMContentLoaded",t)}Ee(po),Qt(po),mo((function(){[].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')).map((function(t){return new fs(t,{delay:{show:500,hide:100}})}))})),mo((function(){document.getElementById("pst-back-to-top").addEventListener("click",(function(){document.body.scrollTop=0,document.documentElement.scrollTop=0}))})),mo((function(){var t=document.getElementById("pst-back-to-top"),e=document.getElementsByClassName("bd-header")[0].getBoundingClientRect();window.addEventListener("scroll",(function(){this.oldScroll>this.scrollY&&this.scrollY>e.bottom?t.style.display="block":t.style.display="none",this.oldScroll=this.scrollY}))})),window.bootstrap=i})(); +//# sourceMappingURL=bootstrap.js.map \ No newline at end of file diff --git a/_static/scripts/bootstrap.js.LICENSE.txt b/_static/scripts/bootstrap.js.LICENSE.txt new file mode 100644 index 0000000..28755c2 --- /dev/null +++ b/_static/scripts/bootstrap.js.LICENSE.txt @@ -0,0 +1,5 @@ +/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ diff --git a/_static/scripts/bootstrap.js.map b/_static/scripts/bootstrap.js.map new file mode 100644 index 0000000..e9e8158 --- /dev/null +++ b/_static/scripts/bootstrap.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/bootstrap.js","mappings":"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","sources":["webpack://pydata_sphinx_theme/webpack/bootstrap","webpack://pydata_sphinx_theme/webpack/runtime/define property getters","webpack://pydata_sphinx_theme/webpack/runtime/hasOwnProperty shorthand","webpack://pydata_sphinx_theme/webpack/runtime/make namespace object","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/enums.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/applyStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getBasePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/math.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/userAgent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isLayoutViewport.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/contains.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isTableElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getParentNode.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/within.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergePaddingObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getFreshSideObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/expandToHashMap.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/arrow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getVariation.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/computeStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/eventListeners.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/rectToClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/detectOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/flip.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/hide.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/offset.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/popperOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/preventOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getAltAxis.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/orderModifiers.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/createPopper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/debounce.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergeByName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper-lite.js","webpack://pydata_sphinx_theme/./node_modules/bootstrap/dist/js/bootstrap.esm.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/mixin.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/bootstrap.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export var top = 'top';\nexport var bottom = 'bottom';\nexport var right = 'right';\nexport var left = 'left';\nexport var auto = 'auto';\nexport var basePlacements = [top, bottom, right, left];\nexport var start = 'start';\nexport var end = 'end';\nexport var clippingParents = 'clippingParents';\nexport var viewport = 'viewport';\nexport var popper = 'popper';\nexport var reference = 'reference';\nexport var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n}, []);\nexport var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n}, []); // modifiers that need to read the DOM\n\nexport var beforeRead = 'beforeRead';\nexport var read = 'read';\nexport var afterRead = 'afterRead'; // pure-logic modifiers\n\nexport var beforeMain = 'beforeMain';\nexport var main = 'main';\nexport var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\nexport var beforeWrite = 'beforeWrite';\nexport var write = 'write';\nexport var afterWrite = 'afterWrite';\nexport var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];","export default function getNodeName(element) {\n return element ? (element.nodeName || '').toLowerCase() : null;\n}","export default function getWindow(node) {\n if (node == null) {\n return window;\n }\n\n if (node.toString() !== '[object Window]') {\n var ownerDocument = node.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView || window : window;\n }\n\n return node;\n}","import getWindow from \"./getWindow.js\";\n\nfunction isElement(node) {\n var OwnElement = getWindow(node).Element;\n return node instanceof OwnElement || node instanceof Element;\n}\n\nfunction isHTMLElement(node) {\n var OwnElement = getWindow(node).HTMLElement;\n return node instanceof OwnElement || node instanceof HTMLElement;\n}\n\nfunction isShadowRoot(node) {\n // IE 11 has no ShadowRoot\n if (typeof ShadowRoot === 'undefined') {\n return false;\n }\n\n var OwnElement = getWindow(node).ShadowRoot;\n return node instanceof OwnElement || node instanceof ShadowRoot;\n}\n\nexport { isElement, isHTMLElement, isShadowRoot };","import getNodeName from \"../dom-utils/getNodeName.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // This modifier takes the styles prepared by the `computeStyles` modifier\n// and applies them to the HTMLElements such as popper and arrow\n\nfunction applyStyles(_ref) {\n var state = _ref.state;\n Object.keys(state.elements).forEach(function (name) {\n var style = state.styles[name] || {};\n var attributes = state.attributes[name] || {};\n var element = state.elements[name]; // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n } // Flow doesn't support to extend this property, but it's the most\n // effective way to apply styles to an HTMLElement\n // $FlowFixMe[cannot-write]\n\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (name) {\n var value = attributes[name];\n\n if (value === false) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value === true ? '' : value);\n }\n });\n });\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state;\n var initialStyles = {\n popper: {\n position: state.options.strategy,\n left: '0',\n top: '0',\n margin: '0'\n },\n arrow: {\n position: 'absolute'\n },\n reference: {}\n };\n Object.assign(state.elements.popper.style, initialStyles.popper);\n state.styles = initialStyles;\n\n if (state.elements.arrow) {\n Object.assign(state.elements.arrow.style, initialStyles.arrow);\n }\n\n return function () {\n Object.keys(state.elements).forEach(function (name) {\n var element = state.elements[name];\n var attributes = state.attributes[name] || {};\n var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n var style = styleProperties.reduce(function (style, property) {\n style[property] = '';\n return style;\n }, {}); // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n }\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (attribute) {\n element.removeAttribute(attribute);\n });\n });\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'applyStyles',\n enabled: true,\n phase: 'write',\n fn: applyStyles,\n effect: effect,\n requires: ['computeStyles']\n};","import { auto } from \"../enums.js\";\nexport default function getBasePlacement(placement) {\n return placement.split('-')[0];\n}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","export default function getUAString() {\n var uaData = navigator.userAgentData;\n\n if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {\n return uaData.brands.map(function (item) {\n return item.brand + \"/\" + item.version;\n }).join(' ');\n }\n\n return navigator.userAgent;\n}","import getUAString from \"../utils/userAgent.js\";\nexport default function isLayoutViewport() {\n return !/^((?!chrome|android).)*safari/i.test(getUAString());\n}","import { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport { round } from \"../utils/math.js\";\nimport getWindow from \"./getWindow.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n\n var clientRect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1;\n\n if (includeScale && isHTMLElement(element)) {\n scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n }\n\n var _ref = isElement(element) ? getWindow(element) : window,\n visualViewport = _ref.visualViewport;\n\n var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n var width = clientRect.width / scaleX;\n var height = clientRect.height / scaleY;\n return {\n width: width,\n height: height,\n top: y,\n right: x + width,\n bottom: y + height,\n left: x,\n x: x,\n y: y\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\"; // Returns the layout rect of an element relative to its offsetParent. Layout\n// means it doesn't take into account transforms.\n\nexport default function getLayoutRect(element) {\n var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n var width = element.offsetWidth;\n var height = element.offsetHeight;\n\n if (Math.abs(clientRect.width - width) <= 1) {\n width = clientRect.width;\n }\n\n if (Math.abs(clientRect.height - height) <= 1) {\n height = clientRect.height;\n }\n\n return {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: width,\n height: height\n };\n}","import { isShadowRoot } from \"./instanceOf.js\";\nexport default function contains(parent, child) {\n var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n if (parent.contains(child)) {\n return true;\n } // then fallback to custom implementation with Shadow DOM support\n else if (rootNode && isShadowRoot(rootNode)) {\n var next = child;\n\n do {\n if (next && parent.isSameNode(next)) {\n return true;\n } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n next = next.parentNode || next.host;\n } while (next);\n } // Give up, the result is false\n\n\n return false;\n}","import getWindow from \"./getWindow.js\";\nexport default function getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}","import getNodeName from \"./getNodeName.js\";\nexport default function isTableElement(element) {\n return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n}","import { isElement } from \"./instanceOf.js\";\nexport default function getDocumentElement(element) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n element.document) || window.document).documentElement;\n}","import getNodeName from \"./getNodeName.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport { isShadowRoot } from \"./instanceOf.js\";\nexport default function getParentNode(element) {\n if (getNodeName(element) === 'html') {\n return element;\n }\n\n return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n // $FlowFixMe[incompatible-return]\n // $FlowFixMe[prop-missing]\n element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n element.parentNode || ( // DOM Element detected\n isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n getDocumentElement(element) // fallback\n\n );\n}","import getWindow from \"./getWindow.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isHTMLElement, isShadowRoot } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getUAString from \"../utils/userAgent.js\";\n\nfunction getTrueOffsetParent(element) {\n if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n getComputedStyle(element).position === 'fixed') {\n return null;\n }\n\n return element.offsetParent;\n} // `.offsetParent` reports `null` for fixed elements, while absolute elements\n// return the containing block\n\n\nfunction getContainingBlock(element) {\n var isFirefox = /firefox/i.test(getUAString());\n var isIE = /Trident/i.test(getUAString());\n\n if (isIE && isHTMLElement(element)) {\n // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n var elementCss = getComputedStyle(element);\n\n if (elementCss.position === 'fixed') {\n return null;\n }\n }\n\n var currentNode = getParentNode(element);\n\n if (isShadowRoot(currentNode)) {\n currentNode = currentNode.host;\n }\n\n while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n // create a containing block.\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n return currentNode;\n } else {\n currentNode = currentNode.parentNode;\n }\n }\n\n return null;\n} // Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\n\n\nexport default function getOffsetParent(element) {\n var window = getWindow(element);\n var offsetParent = getTrueOffsetParent(element);\n\n while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n offsetParent = getTrueOffsetParent(offsetParent);\n }\n\n if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n return window;\n }\n\n return offsetParent || getContainingBlock(element) || window;\n}","export default function getMainAxisFromPlacement(placement) {\n return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n}","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\n}\nexport function withinMaxClamp(min, value, max) {\n var v = within(min, value, max);\n return v > max ? max : v;\n}","import getFreshSideObject from \"./getFreshSideObject.js\";\nexport default function mergePaddingObject(paddingObject) {\n return Object.assign({}, getFreshSideObject(), paddingObject);\n}","export default function getFreshSideObject() {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n };\n}","export default function expandToHashMap(value, keys) {\n return keys.reduce(function (hashMap, key) {\n hashMap[key] = value;\n return hashMap;\n }, {});\n}","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport contains from \"../dom-utils/contains.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport { within } from \"../utils/within.js\";\nimport mergePaddingObject from \"../utils/mergePaddingObject.js\";\nimport expandToHashMap from \"../utils/expandToHashMap.js\";\nimport { left, right, basePlacements, top, bottom } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar toPaddingObject = function toPaddingObject(padding, state) {\n padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n placement: state.placement\n })) : padding;\n return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n};\n\nfunction arrow(_ref) {\n var _state$modifiersData$;\n\n var state = _ref.state,\n name = _ref.name,\n options = _ref.options;\n var arrowElement = state.elements.arrow;\n var popperOffsets = state.modifiersData.popperOffsets;\n var basePlacement = getBasePlacement(state.placement);\n var axis = getMainAxisFromPlacement(basePlacement);\n var isVertical = [left, right].indexOf(basePlacement) >= 0;\n var len = isVertical ? 'height' : 'width';\n\n if (!arrowElement || !popperOffsets) {\n return;\n }\n\n var paddingObject = toPaddingObject(options.padding, state);\n var arrowRect = getLayoutRect(arrowElement);\n var minProp = axis === 'y' ? top : left;\n var maxProp = axis === 'y' ? bottom : right;\n var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n var arrowOffsetParent = getOffsetParent(arrowElement);\n var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n // outside of the popper bounds\n\n var min = paddingObject[minProp];\n var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n var axisProp = axis;\n state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state,\n options = _ref2.options;\n var _options$element = options.element,\n arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n if (arrowElement == null) {\n return;\n } // CSS selector\n\n\n if (typeof arrowElement === 'string') {\n arrowElement = state.elements.popper.querySelector(arrowElement);\n\n if (!arrowElement) {\n return;\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n return;\n }\n\n state.elements.arrow = arrowElement;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'arrow',\n enabled: true,\n phase: 'main',\n fn: arrow,\n effect: effect,\n requires: ['popperOffsets'],\n requiresIfExists: ['preventOverflow']\n};","export default function getVariation(placement) {\n return placement.split('-')[1];\n}","import { top, left, right, bottom, end } from \"../enums.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getWindow from \"../dom-utils/getWindow.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getComputedStyle from \"../dom-utils/getComputedStyle.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport { round } from \"../utils/math.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar unsetSides = {\n top: 'auto',\n right: 'auto',\n bottom: 'auto',\n left: 'auto'\n}; // Round the offsets to the nearest suitable subpixel based on the DPR.\n// Zooming can change the DPR, but it seems to report a value that will\n// cleanly divide the values into the appropriate subpixels.\n\nfunction roundOffsetsByDPR(_ref, win) {\n var x = _ref.x,\n y = _ref.y;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(x * dpr) / dpr || 0,\n y: round(y * dpr) / dpr || 0\n };\n}\n\nexport function mapToStyles(_ref2) {\n var _Object$assign2;\n\n var popper = _ref2.popper,\n popperRect = _ref2.popperRect,\n placement = _ref2.placement,\n variation = _ref2.variation,\n offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets,\n isFixed = _ref2.isFixed;\n var _offsets$x = offsets.x,\n x = _offsets$x === void 0 ? 0 : _offsets$x,\n _offsets$y = offsets.y,\n y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n x: x,\n y: y\n }) : {\n x: x,\n y: y\n };\n\n x = _ref3.x;\n y = _ref3.y;\n var hasX = offsets.hasOwnProperty('x');\n var hasY = offsets.hasOwnProperty('y');\n var sideX = left;\n var sideY = top;\n var win = window;\n\n if (adaptive) {\n var offsetParent = getOffsetParent(popper);\n var heightProp = 'clientHeight';\n var widthProp = 'clientWidth';\n\n if (offsetParent === getWindow(popper)) {\n offsetParent = getDocumentElement(popper);\n\n if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n heightProp = 'scrollHeight';\n widthProp = 'scrollWidth';\n }\n } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n offsetParent = offsetParent;\n\n if (placement === top || (placement === left || placement === right) && variation === end) {\n sideY = bottom;\n var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n offsetParent[heightProp];\n y -= offsetY - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left || (placement === top || placement === bottom) && variation === end) {\n sideX = right;\n var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n offsetParent[widthProp];\n x -= offsetX - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\n\n var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n x: x,\n y: y\n }, getWindow(popper)) : {\n x: x,\n y: y\n };\n\n x = _ref4.x;\n y = _ref4.y;\n\n if (gpuAcceleration) {\n var _Object$assign;\n\n return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n }\n\n return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n}\n\nfunction computeStyles(_ref5) {\n var state = _ref5.state,\n options = _ref5.options;\n var _options$gpuAccelerat = options.gpuAcceleration,\n gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n _options$adaptive = options.adaptive,\n adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n _options$roundOffsets = options.roundOffsets,\n roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n variation: getVariation(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration,\n isFixed: state.options.strategy === 'fixed'\n };\n\n if (state.modifiersData.popperOffsets != null) {\n state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.popperOffsets,\n position: state.options.strategy,\n adaptive: adaptive,\n roundOffsets: roundOffsets\n })));\n }\n\n if (state.modifiersData.arrow != null) {\n state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.arrow,\n position: 'absolute',\n adaptive: false,\n roundOffsets: roundOffsets\n })));\n }\n\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-placement': state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'computeStyles',\n enabled: true,\n phase: 'beforeWrite',\n fn: computeStyles,\n data: {}\n};","import getWindow from \"../dom-utils/getWindow.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar passive = {\n passive: true\n};\n\nfunction effect(_ref) {\n var state = _ref.state,\n instance = _ref.instance,\n options = _ref.options;\n var _options$scroll = options.scroll,\n scroll = _options$scroll === void 0 ? true : _options$scroll,\n _options$resize = options.resize,\n resize = _options$resize === void 0 ? true : _options$resize;\n var window = getWindow(state.elements.popper);\n var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.addEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.addEventListener('resize', instance.update, passive);\n }\n\n return function () {\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.removeEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.removeEventListener('resize', instance.update, passive);\n }\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'eventListeners',\n enabled: true,\n phase: 'write',\n fn: function fn() {},\n effect: effect,\n data: {}\n};","var hash = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nexport default function getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}","var hash = {\n start: 'end',\n end: 'start'\n};\nexport default function getOppositeVariationPlacement(placement) {\n return placement.replace(/start|end/g, function (matched) {\n return hash[matched];\n });\n}","import getWindow from \"./getWindow.js\";\nexport default function getWindowScroll(node) {\n var win = getWindow(node);\n var scrollLeft = win.pageXOffset;\n var scrollTop = win.pageYOffset;\n return {\n scrollLeft: scrollLeft,\n scrollTop: scrollTop\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nexport default function getWindowScrollBarX(element) {\n // If has a CSS width greater than the viewport, then this will be\n // incorrect for RTL.\n // Popper 1 is broken in this case and never had a bug report so let's assume\n // it's not an issue. I don't think anyone ever specifies width on \n // anyway.\n // Browsers where the left scrollbar doesn't cause an issue report `0` for\n // this (e.g. Edge 2019, IE11, Safari)\n return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n}","import getComputedStyle from \"./getComputedStyle.js\";\nexport default function isScrollParent(element) {\n // Firefox wants us to check `-x` and `-y` variations as well\n var _getComputedStyle = getComputedStyle(element),\n overflow = _getComputedStyle.overflow,\n overflowX = _getComputedStyle.overflowX,\n overflowY = _getComputedStyle.overflowY;\n\n return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n}","import getParentNode from \"./getParentNode.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nexport default function getScrollParent(node) {\n if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return node.ownerDocument.body;\n }\n\n if (isHTMLElement(node) && isScrollParent(node)) {\n return node;\n }\n\n return getScrollParent(getParentNode(node));\n}","import getScrollParent from \"./getScrollParent.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getWindow from \"./getWindow.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n/*\ngiven a DOM element, return the list of all scroll parents, up the list of ancesors\nuntil we get to the top window object. This list is what we attach scroll listeners\nto, because if any of these parent elements scroll, we'll need to re-calculate the\nreference element's position.\n*/\n\nexport default function listScrollParents(element, list) {\n var _element$ownerDocumen;\n\n if (list === void 0) {\n list = [];\n }\n\n var scrollParent = getScrollParent(element);\n var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n var win = getWindow(scrollParent);\n var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n var updatedList = list.concat(target);\n return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n updatedList.concat(listScrollParents(getParentNode(target)));\n}","export default function rectToClientRect(rect) {\n return Object.assign({}, rect, {\n left: rect.x,\n top: rect.y,\n right: rect.x + rect.width,\n bottom: rect.y + rect.height\n });\n}","import { viewport } from \"../enums.js\";\nimport getViewportRect from \"./getViewportRect.js\";\nimport getDocumentRect from \"./getDocumentRect.js\";\nimport listScrollParents from \"./listScrollParents.js\";\nimport getOffsetParent from \"./getOffsetParent.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport contains from \"./contains.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport rectToClientRect from \"../utils/rectToClientRect.js\";\nimport { max, min } from \"../utils/math.js\";\n\nfunction getInnerBoundingClientRect(element, strategy) {\n var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n rect.top = rect.top + element.clientTop;\n rect.left = rect.left + element.clientLeft;\n rect.bottom = rect.top + element.clientHeight;\n rect.right = rect.left + element.clientWidth;\n rect.width = element.clientWidth;\n rect.height = element.clientHeight;\n rect.x = rect.left;\n rect.y = rect.top;\n return rect;\n}\n\nfunction getClientRectFromMixedType(element, clippingParent, strategy) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n} // A \"clipping parent\" is an overflowable container with the characteristic of\n// clipping (or hiding) overflowing elements with a position different from\n// `initial`\n\n\nfunction getClippingParents(element) {\n var clippingParents = listScrollParents(getParentNode(element));\n var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n if (!isElement(clipperElement)) {\n return [];\n } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n return clippingParents.filter(function (clippingParent) {\n return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n });\n} // Gets the maximum area that the element is visible in due to any number of\n// clipping parents\n\n\nexport default function getClippingRect(element, boundary, rootBoundary, strategy) {\n var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n var firstClippingParent = clippingParents[0];\n var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n clippingRect.width = clippingRect.right - clippingRect.left;\n clippingRect.height = clippingRect.bottom - clippingRect.top;\n clippingRect.x = clippingRect.left;\n clippingRect.y = clippingRect.top;\n return clippingRect;\n}","import getWindow from \"./getWindow.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getViewportRect(element, strategy) {\n var win = getWindow(element);\n var html = getDocumentElement(element);\n var visualViewport = win.visualViewport;\n var width = html.clientWidth;\n var height = html.clientHeight;\n var x = 0;\n var y = 0;\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n var layoutViewport = isLayoutViewport();\n\n if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n\n return {\n width: width,\n height: height,\n x: x + getWindowScrollBarX(element),\n y: y\n };\n}","import getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nimport { max } from \"../utils/math.js\"; // Gets the entire size of the scrollable document area, even extending outside\n// of the `` and `` rect bounds if horizontally scrollable\n\nexport default function getDocumentRect(element) {\n var _element$ownerDocumen;\n\n var html = getDocumentElement(element);\n var winScroll = getWindowScroll(element);\n var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n var y = -winScroll.scrollTop;\n\n if (getComputedStyle(body || html).direction === 'rtl') {\n x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n }\n\n return {\n width: width,\n height: height,\n x: x,\n y: y\n };\n}","import getBasePlacement from \"./getBasePlacement.js\";\nimport getVariation from \"./getVariation.js\";\nimport getMainAxisFromPlacement from \"./getMainAxisFromPlacement.js\";\nimport { top, right, bottom, left, start, end } from \"../enums.js\";\nexport default function computeOffsets(_ref) {\n var reference = _ref.reference,\n element = _ref.element,\n placement = _ref.placement;\n var basePlacement = placement ? getBasePlacement(placement) : null;\n var variation = placement ? getVariation(placement) : null;\n var commonX = reference.x + reference.width / 2 - element.width / 2;\n var commonY = reference.y + reference.height / 2 - element.height / 2;\n var offsets;\n\n switch (basePlacement) {\n case top:\n offsets = {\n x: commonX,\n y: reference.y - element.height\n };\n break;\n\n case bottom:\n offsets = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n\n case right:\n offsets = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n\n case left:\n offsets = {\n x: reference.x - element.width,\n y: commonY\n };\n break;\n\n default:\n offsets = {\n x: reference.x,\n y: reference.y\n };\n }\n\n var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n if (mainAxis != null) {\n var len = mainAxis === 'y' ? 'height' : 'width';\n\n switch (variation) {\n case start:\n offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n break;\n\n case end:\n offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n break;\n\n default:\n }\n }\n\n return offsets;\n}","import getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport computeOffsets from \"./computeOffsets.js\";\nimport rectToClientRect from \"./rectToClientRect.js\";\nimport { clippingParents, reference, popper, bottom, top, right, basePlacements, viewport } from \"../enums.js\";\nimport { isElement } from \"../dom-utils/instanceOf.js\";\nimport mergePaddingObject from \"./mergePaddingObject.js\";\nimport expandToHashMap from \"./expandToHashMap.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport default function detectOverflow(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n _options$placement = _options.placement,\n placement = _options$placement === void 0 ? state.placement : _options$placement,\n _options$strategy = _options.strategy,\n strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n _options$boundary = _options.boundary,\n boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n _options$rootBoundary = _options.rootBoundary,\n rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n _options$elementConte = _options.elementContext,\n elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n _options$altBoundary = _options.altBoundary,\n altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n _options$padding = _options.padding,\n padding = _options$padding === void 0 ? 0 : _options$padding;\n var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n var altContext = elementContext === popper ? reference : popper;\n var popperRect = state.rects.popper;\n var element = state.elements[altBoundary ? altContext : elementContext];\n var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n var referenceClientRect = getBoundingClientRect(state.elements.reference);\n var popperOffsets = computeOffsets({\n reference: referenceClientRect,\n element: popperRect,\n strategy: 'absolute',\n placement: placement\n });\n var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n // 0 or negative = within the clipping rect\n\n var overflowOffsets = {\n top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n };\n var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n if (elementContext === popper && offsetData) {\n var offset = offsetData[placement];\n Object.keys(overflowOffsets).forEach(function (key) {\n var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n overflowOffsets[key] += offset[axis] * multiply;\n });\n }\n\n return overflowOffsets;\n}","import getOppositePlacement from \"../utils/getOppositePlacement.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getOppositeVariationPlacement from \"../utils/getOppositeVariationPlacement.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport computeAutoPlacement from \"../utils/computeAutoPlacement.js\";\nimport { bottom, top, start, right, left, auto } from \"../enums.js\";\nimport getVariation from \"../utils/getVariation.js\"; // eslint-disable-next-line import/no-unused-modules\n\nfunction getExpandedFallbackPlacements(placement) {\n if (getBasePlacement(placement) === auto) {\n return [];\n }\n\n var oppositePlacement = getOppositePlacement(placement);\n return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n}\n\nfunction flip(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n\n if (state.modifiersData[name]._skip) {\n return;\n }\n\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n specifiedFallbackPlacements = options.fallbackPlacements,\n padding = options.padding,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n _options$flipVariatio = options.flipVariations,\n flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n allowedAutoPlacements = options.allowedAutoPlacements;\n var preferredPlacement = state.options.placement;\n var basePlacement = getBasePlacement(preferredPlacement);\n var isBasePlacement = basePlacement === preferredPlacement;\n var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n flipVariations: flipVariations,\n allowedAutoPlacements: allowedAutoPlacements\n }) : placement);\n }, []);\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var checksMap = new Map();\n var makeFallbackChecks = true;\n var firstFittingPlacement = placements[0];\n\n for (var i = 0; i < placements.length; i++) {\n var placement = placements[i];\n\n var _basePlacement = getBasePlacement(placement);\n\n var isStartVariation = getVariation(placement) === start;\n var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n var len = isVertical ? 'width' : 'height';\n var overflow = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n altBoundary: altBoundary,\n padding: padding\n });\n var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n if (referenceRect[len] > popperRect[len]) {\n mainVariationSide = getOppositePlacement(mainVariationSide);\n }\n\n var altVariationSide = getOppositePlacement(mainVariationSide);\n var checks = [];\n\n if (checkMainAxis) {\n checks.push(overflow[_basePlacement] <= 0);\n }\n\n if (checkAltAxis) {\n checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n }\n\n if (checks.every(function (check) {\n return check;\n })) {\n firstFittingPlacement = placement;\n makeFallbackChecks = false;\n break;\n }\n\n checksMap.set(placement, checks);\n }\n\n if (makeFallbackChecks) {\n // `2` may be desired in some cases – research later\n var numberOfChecks = flipVariations ? 3 : 1;\n\n var _loop = function _loop(_i) {\n var fittingPlacement = placements.find(function (placement) {\n var checks = checksMap.get(placement);\n\n if (checks) {\n return checks.slice(0, _i).every(function (check) {\n return check;\n });\n }\n });\n\n if (fittingPlacement) {\n firstFittingPlacement = fittingPlacement;\n return \"break\";\n }\n };\n\n for (var _i = numberOfChecks; _i > 0; _i--) {\n var _ret = _loop(_i);\n\n if (_ret === \"break\") break;\n }\n }\n\n if (state.placement !== firstFittingPlacement) {\n state.modifiersData[name]._skip = true;\n state.placement = firstFittingPlacement;\n state.reset = true;\n }\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'flip',\n enabled: true,\n phase: 'main',\n fn: flip,\n requiresIfExists: ['offset'],\n data: {\n _skip: false\n }\n};","import getVariation from \"./getVariation.js\";\nimport { variationPlacements, basePlacements, placements as allPlacements } from \"../enums.js\";\nimport detectOverflow from \"./detectOverflow.js\";\nimport getBasePlacement from \"./getBasePlacement.js\";\nexport default function computeAutoPlacement(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n placement = _options.placement,\n boundary = _options.boundary,\n rootBoundary = _options.rootBoundary,\n padding = _options.padding,\n flipVariations = _options.flipVariations,\n _options$allowedAutoP = _options.allowedAutoPlacements,\n allowedAutoPlacements = _options$allowedAutoP === void 0 ? allPlacements : _options$allowedAutoP;\n var variation = getVariation(placement);\n var placements = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n return getVariation(placement) === variation;\n }) : basePlacements;\n var allowedPlacements = placements.filter(function (placement) {\n return allowedAutoPlacements.indexOf(placement) >= 0;\n });\n\n if (allowedPlacements.length === 0) {\n allowedPlacements = placements;\n } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n var overflows = allowedPlacements.reduce(function (acc, placement) {\n acc[placement] = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding\n })[getBasePlacement(placement)];\n return acc;\n }, {});\n return Object.keys(overflows).sort(function (a, b) {\n return overflows[a] - overflows[b];\n });\n}","import { top, bottom, left, right } from \"../enums.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\n\nfunction getSideOffsets(overflow, rect, preventedOffsets) {\n if (preventedOffsets === void 0) {\n preventedOffsets = {\n x: 0,\n y: 0\n };\n }\n\n return {\n top: overflow.top - rect.height - preventedOffsets.y,\n right: overflow.right - rect.width + preventedOffsets.x,\n bottom: overflow.bottom - rect.height + preventedOffsets.y,\n left: overflow.left - rect.width - preventedOffsets.x\n };\n}\n\nfunction isAnySideFullyClipped(overflow) {\n return [top, right, bottom, left].some(function (side) {\n return overflow[side] >= 0;\n });\n}\n\nfunction hide(_ref) {\n var state = _ref.state,\n name = _ref.name;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var preventedOffsets = state.modifiersData.preventOverflow;\n var referenceOverflow = detectOverflow(state, {\n elementContext: 'reference'\n });\n var popperAltOverflow = detectOverflow(state, {\n altBoundary: true\n });\n var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n state.modifiersData[name] = {\n referenceClippingOffsets: referenceClippingOffsets,\n popperEscapeOffsets: popperEscapeOffsets,\n isReferenceHidden: isReferenceHidden,\n hasPopperEscaped: hasPopperEscaped\n };\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-reference-hidden': isReferenceHidden,\n 'data-popper-escaped': hasPopperEscaped\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'hide',\n enabled: true,\n phase: 'main',\n requiresIfExists: ['preventOverflow'],\n fn: hide\n};","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport { top, left, right, placements } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport function distanceAndSkiddingToXY(placement, rects, offset) {\n var basePlacement = getBasePlacement(placement);\n var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n placement: placement\n })) : offset,\n skidding = _ref[0],\n distance = _ref[1];\n\n skidding = skidding || 0;\n distance = (distance || 0) * invertDistance;\n return [left, right].indexOf(basePlacement) >= 0 ? {\n x: distance,\n y: skidding\n } : {\n x: skidding,\n y: distance\n };\n}\n\nfunction offset(_ref2) {\n var state = _ref2.state,\n options = _ref2.options,\n name = _ref2.name;\n var _options$offset = options.offset,\n offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n var data = placements.reduce(function (acc, placement) {\n acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n return acc;\n }, {});\n var _data$state$placement = data[state.placement],\n x = _data$state$placement.x,\n y = _data$state$placement.y;\n\n if (state.modifiersData.popperOffsets != null) {\n state.modifiersData.popperOffsets.x += x;\n state.modifiersData.popperOffsets.y += y;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'offset',\n enabled: true,\n phase: 'main',\n requires: ['popperOffsets'],\n fn: offset\n};","import computeOffsets from \"../utils/computeOffsets.js\";\n\nfunction popperOffsets(_ref) {\n var state = _ref.state,\n name = _ref.name;\n // Offsets are the actual position the popper needs to have to be\n // properly positioned near its reference element\n // This is the most basic placement, and will be adjusted by\n // the modifiers in the next step\n state.modifiersData[name] = computeOffsets({\n reference: state.rects.reference,\n element: state.rects.popper,\n strategy: 'absolute',\n placement: state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'popperOffsets',\n enabled: true,\n phase: 'read',\n fn: popperOffsets,\n data: {}\n};","import { top, left, right, bottom, start } from \"../enums.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport getAltAxis from \"../utils/getAltAxis.js\";\nimport { within, withinMaxClamp } from \"../utils/within.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport getFreshSideObject from \"../utils/getFreshSideObject.js\";\nimport { min as mathMin, max as mathMax } from \"../utils/math.js\";\n\nfunction preventOverflow(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n padding = options.padding,\n _options$tether = options.tether,\n tether = _options$tether === void 0 ? true : _options$tether,\n _options$tetherOffset = options.tetherOffset,\n tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n var overflow = detectOverflow(state, {\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n altBoundary: altBoundary\n });\n var basePlacement = getBasePlacement(state.placement);\n var variation = getVariation(state.placement);\n var isBasePlacement = !variation;\n var mainAxis = getMainAxisFromPlacement(basePlacement);\n var altAxis = getAltAxis(mainAxis);\n var popperOffsets = state.modifiersData.popperOffsets;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n placement: state.placement\n })) : tetherOffset;\n var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n mainAxis: tetherOffsetValue,\n altAxis: tetherOffsetValue\n } : Object.assign({\n mainAxis: 0,\n altAxis: 0\n }, tetherOffsetValue);\n var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n var data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis) {\n var _offsetModifierState$;\n\n var mainSide = mainAxis === 'y' ? top : left;\n var altSide = mainAxis === 'y' ? bottom : right;\n var len = mainAxis === 'y' ? 'height' : 'width';\n var offset = popperOffsets[mainAxis];\n var min = offset + overflow[mainSide];\n var max = offset - overflow[altSide];\n var additive = tether ? -popperRect[len] / 2 : 0;\n var minLen = variation === start ? referenceRect[len] : popperRect[len];\n var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n // outside the reference bounds\n\n var arrowElement = state.elements.arrow;\n var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n width: 0,\n height: 0\n };\n var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n var arrowPaddingMin = arrowPaddingObject[mainSide];\n var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n // to include its full size in the calculation. If the reference is small\n // and near the edge of a boundary, the popper can overflow even if the\n // reference is not overflowing as well (e.g. virtual elements with no\n // width or height)\n\n var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = offset + maxOffset - offsetModifierValue;\n var preventedOffset = within(tether ? mathMin(min, tetherMin) : min, offset, tether ? mathMax(max, tetherMax) : max);\n popperOffsets[mainAxis] = preventedOffset;\n data[mainAxis] = preventedOffset - offset;\n }\n\n if (checkAltAxis) {\n var _offsetModifierState$2;\n\n var _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _len = altAxis === 'y' ? 'height' : 'width';\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'preventOverflow',\n enabled: true,\n phase: 'main',\n fn: preventOverflow,\n requiresIfExists: ['offset']\n};","export default function getAltAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getNodeScroll from \"./getNodeScroll.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport { round } from \"../utils/math.js\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = round(rect.width) / element.offsetWidth || 1;\n var scaleY = round(rect.height) / element.offsetHeight || 1;\n return scaleX !== 1 || scaleY !== 1;\n} // Returns the composite rect of an element relative to its offsetParent.\n// Composite means it takes into account transforms as well as layout.\n\n\nexport default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n\n var isOffsetParentAnElement = isHTMLElement(offsetParent);\n var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n var documentElement = getDocumentElement(offsetParent);\n var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n var scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n var offsets = {\n x: 0,\n y: 0\n };\n\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n isScrollParent(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n\n if (isHTMLElement(offsetParent)) {\n offsets = getBoundingClientRect(offsetParent, true);\n offsets.x += offsetParent.clientLeft;\n offsets.y += offsetParent.clientTop;\n } else if (documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n }\n\n return {\n x: rect.left + scroll.scrollLeft - offsets.x,\n y: rect.top + scroll.scrollTop - offsets.y,\n width: rect.width,\n height: rect.height\n };\n}","import getWindowScroll from \"./getWindowScroll.js\";\nimport getWindow from \"./getWindow.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getHTMLElementScroll from \"./getHTMLElementScroll.js\";\nexport default function getNodeScroll(node) {\n if (node === getWindow(node) || !isHTMLElement(node)) {\n return getWindowScroll(node);\n } else {\n return getHTMLElementScroll(node);\n }\n}","export default function getHTMLElementScroll(element) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n}","import { modifierPhases } from \"../enums.js\"; // source: https://stackoverflow.com/questions/49875255\n\nfunction order(modifiers) {\n var map = new Map();\n var visited = new Set();\n var result = [];\n modifiers.forEach(function (modifier) {\n map.set(modifier.name, modifier);\n }); // On visiting object, check for its dependencies and visit them recursively\n\n function sort(modifier) {\n visited.add(modifier.name);\n var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n requires.forEach(function (dep) {\n if (!visited.has(dep)) {\n var depModifier = map.get(dep);\n\n if (depModifier) {\n sort(depModifier);\n }\n }\n });\n result.push(modifier);\n }\n\n modifiers.forEach(function (modifier) {\n if (!visited.has(modifier.name)) {\n // check for visited object\n sort(modifier);\n }\n });\n return result;\n}\n\nexport default function orderModifiers(modifiers) {\n // order based on dependencies\n var orderedModifiers = order(modifiers); // order based on phase\n\n return modifierPhases.reduce(function (acc, phase) {\n return acc.concat(orderedModifiers.filter(function (modifier) {\n return modifier.phase === phase;\n }));\n }, []);\n}","import getCompositeRect from \"./dom-utils/getCompositeRect.js\";\nimport getLayoutRect from \"./dom-utils/getLayoutRect.js\";\nimport listScrollParents from \"./dom-utils/listScrollParents.js\";\nimport getOffsetParent from \"./dom-utils/getOffsetParent.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nvar DEFAULT_OPTIONS = {\n placement: 'bottom',\n modifiers: [],\n strategy: 'absolute'\n};\n\nfunction areValidElements() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return !args.some(function (element) {\n return !(element && typeof element.getBoundingClientRect === 'function');\n });\n}\n\nexport function popperGenerator(generatorOptions) {\n if (generatorOptions === void 0) {\n generatorOptions = {};\n }\n\n var _generatorOptions = generatorOptions,\n _generatorOptions$def = _generatorOptions.defaultModifiers,\n defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n _generatorOptions$def2 = _generatorOptions.defaultOptions,\n defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n return function createPopper(reference, popper, options) {\n if (options === void 0) {\n options = defaultOptions;\n }\n\n var state = {\n placement: 'bottom',\n orderedModifiers: [],\n options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n modifiersData: {},\n elements: {\n reference: reference,\n popper: popper\n },\n attributes: {},\n styles: {}\n };\n var effectCleanupFns = [];\n var isDestroyed = false;\n var instance = {\n state: state,\n setOptions: function setOptions(setOptionsAction) {\n var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n cleanupModifierEffects();\n state.options = Object.assign({}, defaultOptions, state.options, options);\n state.scrollParents = {\n reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n popper: listScrollParents(popper)\n }; // Orders the modifiers based on their dependencies and `phase`\n // properties\n\n var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n state.orderedModifiers = orderedModifiers.filter(function (m) {\n return m.enabled;\n });\n runModifierEffects();\n return instance.update();\n },\n // Sync update – it will always be executed, even if not necessary. This\n // is useful for low frequency updates where sync behavior simplifies the\n // logic.\n // For high frequency updates (e.g. `resize` and `scroll` events), always\n // prefer the async Popper#update method\n forceUpdate: function forceUpdate() {\n if (isDestroyed) {\n return;\n }\n\n var _state$elements = state.elements,\n reference = _state$elements.reference,\n popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n // anymore\n\n if (!areValidElements(reference, popper)) {\n return;\n } // Store the reference and popper rects to be read by modifiers\n\n\n state.rects = {\n reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n popper: getLayoutRect(popper)\n }; // Modifiers have the ability to reset the current update cycle. The\n // most common use case for this is the `flip` modifier changing the\n // placement, which then needs to re-run all the modifiers, because the\n // logic was previously ran for the previous placement and is therefore\n // stale/incorrect\n\n state.reset = false;\n state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n // is filled with the initial data specified by the modifier. This means\n // it doesn't persist and is fresh on each update.\n // To ensure persistent data, use `${name}#persistent`\n\n state.orderedModifiers.forEach(function (modifier) {\n return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n });\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (state.reset === true) {\n state.reset = false;\n index = -1;\n continue;\n }\n\n var _state$orderedModifie = state.orderedModifiers[index],\n fn = _state$orderedModifie.fn,\n _state$orderedModifie2 = _state$orderedModifie.options,\n _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n name = _state$orderedModifie.name;\n\n if (typeof fn === 'function') {\n state = fn({\n state: state,\n options: _options,\n name: name,\n instance: instance\n }) || state;\n }\n }\n },\n // Async and optimistically optimized update – it will not be executed if\n // not necessary (debounced to run at most once-per-tick)\n update: debounce(function () {\n return new Promise(function (resolve) {\n instance.forceUpdate();\n resolve(state);\n });\n }),\n destroy: function destroy() {\n cleanupModifierEffects();\n isDestroyed = true;\n }\n };\n\n if (!areValidElements(reference, popper)) {\n return instance;\n }\n\n instance.setOptions(options).then(function (state) {\n if (!isDestroyed && options.onFirstUpdate) {\n options.onFirstUpdate(state);\n }\n }); // Modifiers have the ability to execute arbitrary code before the first\n // update cycle runs. They will be executed in the same order as the update\n // cycle. This is useful when a modifier adds some persistent data that\n // other modifiers need to use, but the modifier is run after the dependent\n // one.\n\n function runModifierEffects() {\n state.orderedModifiers.forEach(function (_ref) {\n var name = _ref.name,\n _ref$options = _ref.options,\n options = _ref$options === void 0 ? {} : _ref$options,\n effect = _ref.effect;\n\n if (typeof effect === 'function') {\n var cleanupFn = effect({\n state: state,\n name: name,\n instance: instance,\n options: options\n });\n\n var noopFn = function noopFn() {};\n\n effectCleanupFns.push(cleanupFn || noopFn);\n }\n });\n }\n\n function cleanupModifierEffects() {\n effectCleanupFns.forEach(function (fn) {\n return fn();\n });\n effectCleanupFns = [];\n }\n\n return instance;\n };\n}\nexport var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\nexport { detectOverflow };","export default function debounce(fn) {\n var pending;\n return function () {\n if (!pending) {\n pending = new Promise(function (resolve) {\n Promise.resolve().then(function () {\n pending = undefined;\n resolve(fn());\n });\n });\n }\n\n return pending;\n };\n}","export default function mergeByName(modifiers) {\n var merged = modifiers.reduce(function (merged, current) {\n var existing = merged[current.name];\n merged[current.name] = existing ? Object.assign({}, existing, current, {\n options: Object.assign({}, existing.options, current.options),\n data: Object.assign({}, existing.data, current.data)\n }) : current;\n return merged;\n }, {}); // IE11 does not support Object.values\n\n return Object.keys(merged).map(function (key) {\n return merged[key];\n });\n}","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nimport offset from \"./modifiers/offset.js\";\nimport flip from \"./modifiers/flip.js\";\nimport preventOverflow from \"./modifiers/preventOverflow.js\";\nimport arrow from \"./modifiers/arrow.js\";\nimport hide from \"./modifiers/hide.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles, offset, flip, preventOverflow, arrow, hide];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow }; // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper as createPopperLite } from \"./popper-lite.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport * from \"./modifiers/index.js\";","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow };","/*!\n * Bootstrap v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\nimport * as Popper from '@popperjs/core';\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map();\nconst Data = {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map());\n }\n const instanceMap = elementMap.get(element);\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n return;\n }\n instanceMap.set(key, instance);\n },\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null;\n }\n return null;\n },\n remove(element, key) {\n if (!elementMap.has(element)) {\n return;\n }\n const instanceMap = elementMap.get(element);\n instanceMap.delete(key);\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element);\n }\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000;\nconst MILLISECONDS_MULTIPLIER = 1000;\nconst TRANSITION_END = 'transitionend';\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`);\n }\n return selector;\n};\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`;\n }\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n};\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID);\n } while (document.getElementById(prefix));\n return prefix;\n};\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0;\n }\n\n // Get transition-duration of the element\n let {\n transitionDuration,\n transitionDelay\n } = window.getComputedStyle(element);\n const floatTransitionDuration = Number.parseFloat(transitionDuration);\n const floatTransitionDelay = Number.parseFloat(transitionDelay);\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0;\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0];\n transitionDelay = transitionDelay.split(',')[0];\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n};\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END));\n};\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false;\n }\n if (typeof object.jquery !== 'undefined') {\n object = object[0];\n }\n return typeof object.nodeType !== 'undefined';\n};\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object;\n }\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object));\n }\n return null;\n};\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false;\n }\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible';\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])');\n if (!closedDetails) {\n return elementIsVisible;\n }\n if (closedDetails !== element) {\n const summary = element.closest('summary');\n if (summary && summary.parentNode !== closedDetails) {\n return false;\n }\n if (summary === null) {\n return false;\n }\n }\n return elementIsVisible;\n};\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true;\n }\n if (element.classList.contains('disabled')) {\n return true;\n }\n if (typeof element.disabled !== 'undefined') {\n return element.disabled;\n }\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n};\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null;\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode();\n return root instanceof ShadowRoot ? root : null;\n }\n if (element instanceof ShadowRoot) {\n return element;\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null;\n }\n return findShadowRoot(element.parentNode);\n};\nconst noop = () => {};\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight; // eslint-disable-line no-unused-expressions\n};\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery;\n }\n return null;\n};\nconst DOMContentLoadedCallbacks = [];\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback();\n }\n });\n }\n DOMContentLoadedCallbacks.push(callback);\n } else {\n callback();\n }\n};\nconst isRTL = () => document.documentElement.dir === 'rtl';\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery();\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME;\n const JQUERY_NO_CONFLICT = $.fn[name];\n $.fn[name] = plugin.jQueryInterface;\n $.fn[name].Constructor = plugin;\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT;\n return plugin.jQueryInterface;\n };\n }\n });\n};\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue;\n};\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback);\n return;\n }\n const durationPadding = 5;\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n let called = false;\n const handler = ({\n target\n }) => {\n if (target !== transitionElement) {\n return;\n }\n called = true;\n transitionElement.removeEventListener(TRANSITION_END, handler);\n execute(callback);\n };\n transitionElement.addEventListener(TRANSITION_END, handler);\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement);\n }\n }, emulatedDuration);\n};\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length;\n let index = list.indexOf(activeElement);\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n }\n index += shouldGetNext ? 1 : -1;\n if (isCycleAllowed) {\n index = (index + listLength) % listLength;\n }\n return list[Math.max(0, Math.min(index, listLength - 1))];\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\nconst stripNameRegex = /\\..*/;\nconst stripUidRegex = /::\\d+$/;\nconst eventRegistry = {}; // Events storage\nlet uidEvent = 1;\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n};\nconst nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n}\nfunction getElementEvents(element) {\n const uid = makeEventUid(element);\n element.uidEvent = uid;\n eventRegistry[uid] = eventRegistry[uid] || {};\n return eventRegistry[uid];\n}\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, {\n delegateTarget: element\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn);\n }\n return fn.apply(element, [event]);\n };\n}\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector);\n for (let {\n target\n } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue;\n }\n hydrateObj(event, {\n delegateTarget: target\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn);\n }\n return fn.apply(target, [event]);\n }\n }\n };\n}\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n}\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string';\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n let typeEvent = getTypeEvent(originalTypeEvent);\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent;\n }\n return [isDelegated, callable, typeEvent];\n}\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n return fn.call(this, event);\n }\n };\n };\n callable = wrapFunction(callable);\n }\n const events = getElementEvents(element);\n const handlers = events[typeEvent] || (events[typeEvent] = {});\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff;\n return;\n }\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n fn.delegationSelector = isDelegated ? handler : null;\n fn.callable = callable;\n fn.oneOff = oneOff;\n fn.uidEvent = uid;\n handlers[uid] = fn;\n element.addEventListener(typeEvent, fn, isDelegated);\n}\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector);\n if (!fn) {\n return;\n }\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n delete events[typeEvent][fn.uidEvent];\n}\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {};\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n}\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '');\n return customEvents[event] || event;\n}\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false);\n },\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true);\n },\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n const inNamespace = typeEvent !== originalTypeEvent;\n const events = getElementEvents(element);\n const storeElementEvent = events[typeEvent] || {};\n const isNamespace = originalTypeEvent.startsWith('.');\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return;\n }\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n return;\n }\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n }\n }\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '');\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n },\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null;\n }\n const $ = getjQuery();\n const typeEvent = getTypeEvent(event);\n const inNamespace = event !== typeEvent;\n let jQueryEvent = null;\n let bubbles = true;\n let nativeDispatch = true;\n let defaultPrevented = false;\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args);\n $(element).trigger(jQueryEvent);\n bubbles = !jQueryEvent.isPropagationStopped();\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n defaultPrevented = jQueryEvent.isDefaultPrevented();\n }\n const evt = hydrateObj(new Event(event, {\n bubbles,\n cancelable: true\n }), args);\n if (defaultPrevented) {\n evt.preventDefault();\n }\n if (nativeDispatch) {\n element.dispatchEvent(evt);\n }\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault();\n }\n return evt;\n }\n};\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value;\n } catch (_unused) {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value;\n }\n });\n }\n }\n return obj;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n if (value === Number(value).toString()) {\n return Number(value);\n }\n if (value === '' || value === 'null') {\n return null;\n }\n if (typeof value !== 'string') {\n return value;\n }\n try {\n return JSON.parse(decodeURIComponent(value));\n } catch (_unused) {\n return value;\n }\n}\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n}\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n },\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n },\n getDataAttributes(element) {\n if (!element) {\n return {};\n }\n const attributes = {};\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '');\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n attributes[pureKey] = normalizeData(element.dataset[key]);\n }\n return attributes;\n },\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {};\n }\n static get DefaultType() {\n return {};\n }\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!');\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n return config;\n }\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n };\n }\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property];\n const valueType = isElement(value) ? 'element' : toType(value);\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n }\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.3';\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super();\n element = getElement(element);\n if (!element) {\n return;\n }\n this._element = element;\n this._config = this._getConfig(config);\n Data.set(this._element, this.constructor.DATA_KEY, this);\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY);\n EventHandler.off(this._element, this.constructor.EVENT_KEY);\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null;\n }\n }\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated);\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY);\n }\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n }\n static get VERSION() {\n return VERSION;\n }\n static get DATA_KEY() {\n return `bs.${this.NAME}`;\n }\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`;\n }\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target');\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href');\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n return null;\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n }\n selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;\n }\n return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null;\n};\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n },\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector);\n },\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector));\n },\n parents(element, selector) {\n const parents = [];\n let ancestor = element.parentNode.closest(selector);\n while (ancestor) {\n parents.push(ancestor);\n ancestor = ancestor.parentNode.closest(selector);\n }\n return parents;\n },\n prev(element, selector) {\n let previous = element.previousElementSibling;\n while (previous) {\n if (previous.matches(selector)) {\n return [previous];\n }\n previous = previous.previousElementSibling;\n }\n return [];\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling;\n while (next) {\n if (next.matches(selector)) {\n return [next];\n }\n next = next.nextElementSibling;\n }\n return [];\n },\n focusableChildren(element) {\n const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));\n },\n getSelectorFromElement(element) {\n const selector = getSelector(element);\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null;\n }\n return null;\n },\n getElementFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.findOne(selector) : null;\n },\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.find(selector) : [];\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n const name = component.NAME;\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`);\n const instance = component.getOrCreateInstance(target);\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]();\n });\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$f = 'alert';\nconst DATA_KEY$a = 'bs.alert';\nconst EVENT_KEY$b = `.${DATA_KEY$a}`;\nconst EVENT_CLOSE = `close${EVENT_KEY$b}`;\nconst EVENT_CLOSED = `closed${EVENT_KEY$b}`;\nconst CLASS_NAME_FADE$5 = 'fade';\nconst CLASS_NAME_SHOW$8 = 'show';\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$f;\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n if (closeEvent.defaultPrevented) {\n return;\n }\n this._element.classList.remove(CLASS_NAME_SHOW$8);\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n }\n\n // Private\n _destroyElement() {\n this._element.remove();\n EventHandler.trigger(this._element, EVENT_CLOSED);\n this.dispose();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close');\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$e = 'button';\nconst DATA_KEY$9 = 'bs.button';\nconst EVENT_KEY$a = `.${DATA_KEY$9}`;\nconst DATA_API_KEY$6 = '.data-api';\nconst CLASS_NAME_ACTIVE$3 = 'active';\nconst SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle=\"button\"]';\nconst EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$e;\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3));\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this);\n if (config === 'toggle') {\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => {\n event.preventDefault();\n const button = event.target.closest(SELECTOR_DATA_TOGGLE$5);\n const data = Button.getOrCreateInstance(button);\n data.toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$d = 'swipe';\nconst EVENT_KEY$9 = '.bs.swipe';\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`;\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`;\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`;\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`;\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`;\nconst POINTER_TYPE_TOUCH = 'touch';\nconst POINTER_TYPE_PEN = 'pen';\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event';\nconst SWIPE_THRESHOLD = 40;\nconst Default$c = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n};\nconst DefaultType$c = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n};\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super();\n this._element = element;\n if (!element || !Swipe.isSupported()) {\n return;\n }\n this._config = this._getConfig(config);\n this._deltaX = 0;\n this._supportPointerEvents = Boolean(window.PointerEvent);\n this._initEvents();\n }\n\n // Getters\n static get Default() {\n return Default$c;\n }\n static get DefaultType() {\n return DefaultType$c;\n }\n static get NAME() {\n return NAME$d;\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY$9);\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX;\n return;\n }\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX;\n }\n }\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX;\n }\n this._handleSwipe();\n execute(this._config.endCallback);\n }\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n }\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX);\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return;\n }\n const direction = absDeltaX / this._deltaX;\n this._deltaX = 0;\n if (!direction) {\n return;\n }\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n }\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n }\n }\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$c = 'carousel';\nconst DATA_KEY$8 = 'bs.carousel';\nconst EVENT_KEY$8 = `.${DATA_KEY$8}`;\nconst DATA_API_KEY$5 = '.data-api';\nconst ARROW_LEFT_KEY$1 = 'ArrowLeft';\nconst ARROW_RIGHT_KEY$1 = 'ArrowRight';\nconst TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next';\nconst ORDER_PREV = 'prev';\nconst DIRECTION_LEFT = 'left';\nconst DIRECTION_RIGHT = 'right';\nconst EVENT_SLIDE = `slide${EVENT_KEY$8}`;\nconst EVENT_SLID = `slid${EVENT_KEY$8}`;\nconst EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`;\nconst EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`;\nconst EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`;\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`;\nconst EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst CLASS_NAME_CAROUSEL = 'carousel';\nconst CLASS_NAME_ACTIVE$2 = 'active';\nconst CLASS_NAME_SLIDE = 'slide';\nconst CLASS_NAME_END = 'carousel-item-end';\nconst CLASS_NAME_START = 'carousel-item-start';\nconst CLASS_NAME_NEXT = 'carousel-item-next';\nconst CLASS_NAME_PREV = 'carousel-item-prev';\nconst SELECTOR_ACTIVE = '.active';\nconst SELECTOR_ITEM = '.carousel-item';\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\nconst SELECTOR_ITEM_IMG = '.carousel-item img';\nconst SELECTOR_INDICATORS = '.carousel-indicators';\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT\n};\nconst Default$b = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n};\nconst DefaultType$b = {\n interval: '(number|boolean)',\n // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._interval = null;\n this._activeElement = null;\n this._isSliding = false;\n this.touchTimeout = null;\n this._swipeHelper = null;\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n this._addEventListeners();\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$b;\n }\n static get DefaultType() {\n return DefaultType$b;\n }\n static get NAME() {\n return NAME$c;\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT);\n }\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next();\n }\n }\n prev() {\n this._slide(ORDER_PREV);\n }\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element);\n }\n this._clearInterval();\n }\n cycle() {\n this._clearInterval();\n this._updateInterval();\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n }\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n return;\n }\n this.cycle();\n }\n to(index) {\n const items = this._getItems();\n if (index > items.length - 1 || index < 0) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n return;\n }\n const activeIndex = this._getItemIndex(this._getActive());\n if (activeIndex === index) {\n return;\n }\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n this._slide(order, items[index]);\n }\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose();\n }\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval;\n return config;\n }\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event));\n }\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause());\n EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle());\n }\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners();\n }\n }\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n }\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return;\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause();\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout);\n }\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n };\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n };\n this._swipeHelper = new Swipe(this._element, swipeConfig);\n }\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return;\n }\n const direction = KEY_TO_DIRECTION[event.key];\n if (direction) {\n event.preventDefault();\n this._slide(this._directionToOrder(direction));\n }\n }\n _getItemIndex(element) {\n return this._getItems().indexOf(element);\n }\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return;\n }\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2);\n activeIndicator.removeAttribute('aria-current');\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2);\n newActiveIndicator.setAttribute('aria-current', 'true');\n }\n }\n _updateInterval() {\n const element = this._activeElement || this._getActive();\n if (!element) {\n return;\n }\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n this._config.interval = elementInterval || this._config.defaultInterval;\n }\n _slide(order, element = null) {\n if (this._isSliding) {\n return;\n }\n const activeElement = this._getActive();\n const isNext = order === ORDER_NEXT;\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n if (nextElement === activeElement) {\n return;\n }\n const nextElementIndex = this._getItemIndex(nextElement);\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n });\n };\n const slideEvent = triggerEvent(EVENT_SLIDE);\n if (slideEvent.defaultPrevented) {\n return;\n }\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return;\n }\n const isCycling = Boolean(this._interval);\n this.pause();\n this._isSliding = true;\n this._setActiveIndicatorElement(nextElementIndex);\n this._activeElement = nextElement;\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n nextElement.classList.add(orderClassName);\n reflow(nextElement);\n activeElement.classList.add(directionalClassName);\n nextElement.classList.add(directionalClassName);\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName);\n nextElement.classList.add(CLASS_NAME_ACTIVE$2);\n activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName);\n this._isSliding = false;\n triggerEvent(EVENT_SLID);\n };\n this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n if (isCycling) {\n this.cycle();\n }\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE);\n }\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n }\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element);\n }\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval);\n this._interval = null;\n }\n }\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n }\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n }\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n }\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config);\n if (typeof config === 'number') {\n data.to(config);\n return;\n }\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return;\n }\n event.preventDefault();\n const carousel = Carousel.getOrCreateInstance(target);\n const slideIndex = this.getAttribute('data-bs-slide-to');\n if (slideIndex) {\n carousel.to(slideIndex);\n carousel._maybeEnableCycle();\n return;\n }\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next();\n carousel._maybeEnableCycle();\n return;\n }\n carousel.prev();\n carousel._maybeEnableCycle();\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel);\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$b = 'collapse';\nconst DATA_KEY$7 = 'bs.collapse';\nconst EVENT_KEY$7 = `.${DATA_KEY$7}`;\nconst DATA_API_KEY$4 = '.data-api';\nconst EVENT_SHOW$6 = `show${EVENT_KEY$7}`;\nconst EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`;\nconst EVENT_HIDE$6 = `hide${EVENT_KEY$7}`;\nconst EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`;\nconst EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`;\nconst CLASS_NAME_SHOW$7 = 'show';\nconst CLASS_NAME_COLLAPSE = 'collapse';\nconst CLASS_NAME_COLLAPSING = 'collapsing';\nconst CLASS_NAME_COLLAPSED = 'collapsed';\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\nconst WIDTH = 'width';\nconst HEIGHT = 'height';\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\nconst SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle=\"collapse\"]';\nconst Default$a = {\n parent: null,\n toggle: true\n};\nconst DefaultType$a = {\n parent: '(null|element)',\n toggle: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isTransitioning = false;\n this._triggerArray = [];\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4);\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem);\n const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem);\n }\n }\n this._initializeChildren();\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n }\n if (this._config.toggle) {\n this.toggle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$a;\n }\n static get DefaultType() {\n return DefaultType$a;\n }\n static get NAME() {\n return NAME$b;\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide();\n } else {\n this.show();\n }\n }\n show() {\n if (this._isTransitioning || this._isShown()) {\n return;\n }\n let activeChildren = [];\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n toggle: false\n }));\n }\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n for (const activeInstance of activeChildren) {\n activeInstance.hide();\n }\n const dimension = this._getDimension();\n this._element.classList.remove(CLASS_NAME_COLLAPSE);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.style[dimension] = 0;\n this._addAriaAndCollapsedClass(this._triggerArray, true);\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n this._element.style[dimension] = '';\n EventHandler.trigger(this._element, EVENT_SHOWN$6);\n };\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n const scrollSize = `scroll${capitalizedDimension}`;\n this._queueCallback(complete, this._element, true);\n this._element.style[dimension] = `${this._element[scrollSize]}px`;\n }\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n const dimension = this._getDimension();\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger);\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false);\n }\n }\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE);\n EventHandler.trigger(this._element, EVENT_HIDDEN$6);\n };\n this._element.style[dimension] = '';\n this._queueCallback(complete, this._element, true);\n }\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW$7);\n }\n\n // Private\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle); // Coerce string values\n config.parent = getElement(config.parent);\n return config;\n }\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n }\n _initializeChildren() {\n if (!this._config.parent) {\n return;\n }\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4);\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element);\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected));\n }\n }\n }\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n }\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return;\n }\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n element.setAttribute('aria-expanded', isOpen);\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {};\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false;\n }\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config);\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n event.preventDefault();\n }\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, {\n toggle: false\n }).toggle();\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$a = 'dropdown';\nconst DATA_KEY$6 = 'bs.dropdown';\nconst EVENT_KEY$6 = `.${DATA_KEY$6}`;\nconst DATA_API_KEY$3 = '.data-api';\nconst ESCAPE_KEY$2 = 'Escape';\nconst TAB_KEY$1 = 'Tab';\nconst ARROW_UP_KEY$1 = 'ArrowUp';\nconst ARROW_DOWN_KEY$1 = 'ArrowDown';\nconst RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE$5 = `hide${EVENT_KEY$6}`;\nconst EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`;\nconst EVENT_SHOW$5 = `show${EVENT_KEY$6}`;\nconst EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`;\nconst EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst CLASS_NAME_SHOW$6 = 'show';\nconst CLASS_NAME_DROPUP = 'dropup';\nconst CLASS_NAME_DROPEND = 'dropend';\nconst CLASS_NAME_DROPSTART = 'dropstart';\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center';\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\nconst SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`;\nconst SELECTOR_MENU = '.dropdown-menu';\nconst SELECTOR_NAVBAR = '.navbar';\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav';\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';\nconst PLACEMENT_TOPCENTER = 'top';\nconst PLACEMENT_BOTTOMCENTER = 'bottom';\nconst Default$9 = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n};\nconst DefaultType$9 = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n};\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._popper = null;\n this._parent = this._element.parentNode; // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n this._inNavbar = this._detectNavbar();\n }\n\n // Getters\n static get Default() {\n return Default$9;\n }\n static get DefaultType() {\n return DefaultType$9;\n }\n static get NAME() {\n return NAME$a;\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show();\n }\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget);\n if (showEvent.defaultPrevented) {\n return;\n }\n this._createPopper();\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n this._element.focus();\n this._element.setAttribute('aria-expanded', true);\n this._menu.classList.add(CLASS_NAME_SHOW$6);\n this._element.classList.add(CLASS_NAME_SHOW$6);\n EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget);\n }\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n this._completeHide(relatedTarget);\n }\n dispose() {\n if (this._popper) {\n this._popper.destroy();\n }\n super.dispose();\n }\n update() {\n this._inNavbar = this._detectNavbar();\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n if (this._popper) {\n this._popper.destroy();\n }\n this._menu.classList.remove(CLASS_NAME_SHOW$6);\n this._element.classList.remove(CLASS_NAME_SHOW$6);\n this._element.setAttribute('aria-expanded', 'false');\n Manipulator.removeDataAttribute(this._menu, 'popper');\n EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);\n }\n _getConfig(config) {\n config = super._getConfig(config);\n if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME$a.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n }\n return config;\n }\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n }\n let referenceElement = this._element;\n if (this._config.reference === 'parent') {\n referenceElement = this._parent;\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference);\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference;\n }\n const popperConfig = this._getPopperConfig();\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig);\n }\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW$6);\n }\n _getPlacement() {\n const parentDropdown = this._parent;\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER;\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n }\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n }\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null;\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n };\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }];\n }\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _selectMenuItem({\n key,\n target\n }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element));\n if (!items.length) {\n return;\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) {\n return;\n }\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle);\n if (!context || context._config.autoClose === false) {\n continue;\n }\n const composedPath = event.composedPath();\n const isMenuTarget = composedPath.includes(context._menu);\n if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n continue;\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue;\n }\n const relatedTarget = {\n relatedTarget: context._element\n };\n if (event.type === 'click') {\n relatedTarget.clickEvent = event;\n }\n context._completeHide(relatedTarget);\n }\n }\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName);\n const isEscapeEvent = event.key === ESCAPE_KEY$2;\n const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key);\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return;\n }\n if (isInput && !isEscapeEvent) {\n return;\n }\n event.preventDefault();\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode);\n const instance = Dropdown.getOrCreateInstance(getToggleButton);\n if (isUpOrDownEvent) {\n event.stopPropagation();\n instance.show();\n instance._selectMenuItem(event);\n return;\n }\n if (instance._isShown()) {\n // else is escape and we check if it is shown\n event.stopPropagation();\n instance.hide();\n getToggleButton.focus();\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {\n event.preventDefault();\n Dropdown.getOrCreateInstance(this).toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$9 = 'backdrop';\nconst CLASS_NAME_FADE$4 = 'fade';\nconst CLASS_NAME_SHOW$5 = 'show';\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`;\nconst Default$8 = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true,\n // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n};\nconst DefaultType$8 = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n};\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isAppended = false;\n this._element = null;\n }\n\n // Getters\n static get Default() {\n return Default$8;\n }\n static get DefaultType() {\n return DefaultType$8;\n }\n static get NAME() {\n return NAME$9;\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._append();\n const element = this._getElement();\n if (this._config.isAnimated) {\n reflow(element);\n }\n element.classList.add(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n execute(callback);\n });\n }\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._getElement().classList.remove(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n this.dispose();\n execute(callback);\n });\n }\n dispose() {\n if (!this._isAppended) {\n return;\n }\n EventHandler.off(this._element, EVENT_MOUSEDOWN);\n this._element.remove();\n this._isAppended = false;\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div');\n backdrop.className = this._config.className;\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE$4);\n }\n this._element = backdrop;\n }\n return this._element;\n }\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement);\n return config;\n }\n _append() {\n if (this._isAppended) {\n return;\n }\n const element = this._getElement();\n this._config.rootElement.append(element);\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback);\n });\n this._isAppended = true;\n }\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$8 = 'focustrap';\nconst DATA_KEY$5 = 'bs.focustrap';\nconst EVENT_KEY$5 = `.${DATA_KEY$5}`;\nconst EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`;\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`;\nconst TAB_KEY = 'Tab';\nconst TAB_NAV_FORWARD = 'forward';\nconst TAB_NAV_BACKWARD = 'backward';\nconst Default$7 = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n};\nconst DefaultType$7 = {\n autofocus: 'boolean',\n trapElement: 'element'\n};\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isActive = false;\n this._lastTabNavDirection = null;\n }\n\n // Getters\n static get Default() {\n return Default$7;\n }\n static get DefaultType() {\n return DefaultType$7;\n }\n static get NAME() {\n return NAME$8;\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return;\n }\n if (this._config.autofocus) {\n this._config.trapElement.focus();\n }\n EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event));\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n this._isActive = true;\n }\n deactivate() {\n if (!this._isActive) {\n return;\n }\n this._isActive = false;\n EventHandler.off(document, EVENT_KEY$5);\n }\n\n // Private\n _handleFocusin(event) {\n const {\n trapElement\n } = this._config;\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return;\n }\n const elements = SelectorEngine.focusableChildren(trapElement);\n if (elements.length === 0) {\n trapElement.focus();\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus();\n } else {\n elements[0].focus();\n }\n }\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return;\n }\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\nconst SELECTOR_STICKY_CONTENT = '.sticky-top';\nconst PROPERTY_PADDING = 'padding-right';\nconst PROPERTY_MARGIN = 'margin-right';\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body;\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth;\n return Math.abs(window.innerWidth - documentWidth);\n }\n hide() {\n const width = this.getWidth();\n this._disableOverFlow();\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n }\n reset() {\n this._resetElementAttributes(this._element, 'overflow');\n this._resetElementAttributes(this._element, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n }\n isOverflowing() {\n return this.getWidth() > 0;\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow');\n this._element.style.overflow = 'hidden';\n }\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth();\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return;\n }\n this._saveInitialAttribute(element, styleProperty);\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty);\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue);\n }\n }\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty);\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty);\n return;\n }\n Manipulator.removeDataAttribute(element, styleProperty);\n element.style.setProperty(styleProperty, value);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector);\n return;\n }\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel);\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$7 = 'modal';\nconst DATA_KEY$4 = 'bs.modal';\nconst EVENT_KEY$4 = `.${DATA_KEY$4}`;\nconst DATA_API_KEY$2 = '.data-api';\nconst ESCAPE_KEY$1 = 'Escape';\nconst EVENT_HIDE$4 = `hide${EVENT_KEY$4}`;\nconst EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`;\nconst EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`;\nconst EVENT_SHOW$4 = `show${EVENT_KEY$4}`;\nconst EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`;\nconst EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`;\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`;\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`;\nconst EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`;\nconst EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`;\nconst CLASS_NAME_OPEN = 'modal-open';\nconst CLASS_NAME_FADE$3 = 'fade';\nconst CLASS_NAME_SHOW$4 = 'show';\nconst CLASS_NAME_STATIC = 'modal-static';\nconst OPEN_SELECTOR$1 = '.modal.show';\nconst SELECTOR_DIALOG = '.modal-dialog';\nconst SELECTOR_MODAL_BODY = '.modal-body';\nconst SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle=\"modal\"]';\nconst Default$6 = {\n backdrop: true,\n focus: true,\n keyboard: true\n};\nconst DefaultType$6 = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._isShown = false;\n this._isTransitioning = false;\n this._scrollBar = new ScrollBarHelper();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$6;\n }\n static get DefaultType() {\n return DefaultType$6;\n }\n static get NAME() {\n return NAME$7;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._isTransitioning = true;\n this._scrollBar.hide();\n document.body.classList.add(CLASS_NAME_OPEN);\n this._adjustDialog();\n this._backdrop.show(() => this._showElement(relatedTarget));\n }\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._isShown = false;\n this._isTransitioning = true;\n this._focustrap.deactivate();\n this._element.classList.remove(CLASS_NAME_SHOW$4);\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n }\n dispose() {\n EventHandler.off(window, EVENT_KEY$4);\n EventHandler.off(this._dialog, EVENT_KEY$4);\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n handleUpdate() {\n this._adjustDialog();\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop),\n // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element);\n }\n this._element.style.display = 'block';\n this._element.removeAttribute('aria-hidden');\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.scrollTop = 0;\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n if (modalBody) {\n modalBody.scrollTop = 0;\n }\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_SHOW$4);\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate();\n }\n this._isTransitioning = false;\n EventHandler.trigger(this._element, EVENT_SHOWN$4, {\n relatedTarget\n });\n };\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => {\n if (event.key !== ESCAPE_KEY$1) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n this._triggerBackdropTransition();\n });\n EventHandler.on(window, EVENT_RESIZE$1, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog();\n }\n });\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return;\n }\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition();\n return;\n }\n if (this._config.backdrop) {\n this.hide();\n }\n });\n });\n }\n _hideModal() {\n this._element.style.display = 'none';\n this._element.setAttribute('aria-hidden', true);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n this._isTransitioning = false;\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN);\n this._resetAdjustments();\n this._scrollBar.reset();\n EventHandler.trigger(this._element, EVENT_HIDDEN$4);\n });\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE$3);\n }\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1);\n if (hideEvent.defaultPrevented) {\n return;\n }\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const initialOverflowY = this._element.style.overflowY;\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return;\n }\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden';\n }\n this._element.classList.add(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY;\n }, this._dialog);\n }, this._dialog);\n this._element.focus();\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const scrollbarWidth = this._scrollBar.getWidth();\n const isBodyOverflowing = scrollbarWidth > 0;\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n }\n _resetAdjustments() {\n this._element.style.paddingLeft = '';\n this._element.style.paddingRight = '';\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](relatedTarget);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n EventHandler.one(target, EVENT_SHOW$4, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$4, () => {\n if (isVisible(this)) {\n this.focus();\n }\n });\n });\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1);\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide();\n }\n const data = Modal.getOrCreateInstance(target);\n data.toggle(this);\n});\nenableDismissTrigger(Modal);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$6 = 'offcanvas';\nconst DATA_KEY$3 = 'bs.offcanvas';\nconst EVENT_KEY$3 = `.${DATA_KEY$3}`;\nconst DATA_API_KEY$1 = '.data-api';\nconst EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst ESCAPE_KEY = 'Escape';\nconst CLASS_NAME_SHOW$3 = 'show';\nconst CLASS_NAME_SHOWING$1 = 'showing';\nconst CLASS_NAME_HIDING = 'hiding';\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\nconst OPEN_SELECTOR = '.offcanvas.show';\nconst EVENT_SHOW$3 = `show${EVENT_KEY$3}`;\nconst EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`;\nconst EVENT_HIDE$3 = `hide${EVENT_KEY$3}`;\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`;\nconst EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`;\nconst EVENT_RESIZE = `resize${EVENT_KEY$3}`;\nconst EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`;\nconst SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle=\"offcanvas\"]';\nconst Default$5 = {\n backdrop: true,\n keyboard: true,\n scroll: false\n};\nconst DefaultType$5 = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isShown = false;\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$5;\n }\n static get DefaultType() {\n return DefaultType$5;\n }\n static get NAME() {\n return NAME$6;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._backdrop.show();\n if (!this._config.scroll) {\n new ScrollBarHelper().hide();\n }\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.classList.add(CLASS_NAME_SHOWING$1);\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate();\n }\n this._element.classList.add(CLASS_NAME_SHOW$3);\n this._element.classList.remove(CLASS_NAME_SHOWING$1);\n EventHandler.trigger(this._element, EVENT_SHOWN$3, {\n relatedTarget\n });\n };\n this._queueCallback(completeCallBack, this._element, true);\n }\n hide() {\n if (!this._isShown) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._focustrap.deactivate();\n this._element.blur();\n this._isShown = false;\n this._element.classList.add(CLASS_NAME_HIDING);\n this._backdrop.hide();\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n if (!this._config.scroll) {\n new ScrollBarHelper().reset();\n }\n EventHandler.trigger(this._element, EVENT_HIDDEN$3);\n };\n this._queueCallback(completeCallback, this._element, true);\n }\n dispose() {\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n return;\n }\n this.hide();\n };\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop);\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n });\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$3, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus();\n }\n });\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide();\n }\n const data = Offcanvas.getOrCreateInstance(target);\n data.toggle(this);\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$2, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show();\n }\n});\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide();\n }\n }\n});\nenableDismissTrigger(Offcanvas);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\nconst DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n dd: [],\n div: [],\n dl: [],\n dt: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n};\n// js-docs-end allow-list\n\nconst uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\n// eslint-disable-next-line unicorn/better-regex\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase();\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue));\n }\n return true;\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n};\nfunction sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml;\n }\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml);\n }\n const domParser = new window.DOMParser();\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase();\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove();\n continue;\n }\n const attributeList = [].concat(...element.attributes);\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName);\n }\n }\n }\n return createdDocument.body.innerHTML;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$5 = 'TemplateFactory';\nconst Default$4 = {\n allowList: DefaultAllowlist,\n content: {},\n // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '
'\n};\nconst DefaultType$4 = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n};\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n};\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n }\n\n // Getters\n static get Default() {\n return Default$4;\n }\n static get DefaultType() {\n return DefaultType$4;\n }\n static get NAME() {\n return NAME$5;\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n }\n hasContent() {\n return this.getContent().length > 0;\n }\n changeContent(content) {\n this._checkContent(content);\n this._config.content = {\n ...this._config.content,\n ...content\n };\n return this;\n }\n toHtml() {\n const templateWrapper = document.createElement('div');\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector);\n }\n const template = templateWrapper.children[0];\n const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n if (extraClass) {\n template.classList.add(...extraClass.split(' '));\n }\n return template;\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config);\n this._checkContent(config.content);\n }\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({\n selector,\n entry: content\n }, DefaultContentType);\n }\n }\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template);\n if (!templateElement) {\n return;\n }\n content = this._resolvePossibleFunction(content);\n if (!content) {\n templateElement.remove();\n return;\n }\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement);\n return;\n }\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content);\n return;\n }\n templateElement.textContent = content;\n }\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this]);\n }\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = '';\n templateElement.append(element);\n return;\n }\n templateElement.textContent = element.textContent;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$4 = 'tooltip';\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\nconst CLASS_NAME_FADE$2 = 'fade';\nconst CLASS_NAME_MODAL = 'modal';\nconst CLASS_NAME_SHOW$2 = 'show';\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\nconst EVENT_MODAL_HIDE = 'hide.bs.modal';\nconst TRIGGER_HOVER = 'hover';\nconst TRIGGER_FOCUS = 'focus';\nconst TRIGGER_CLICK = 'click';\nconst TRIGGER_MANUAL = 'manual';\nconst EVENT_HIDE$2 = 'hide';\nconst EVENT_HIDDEN$2 = 'hidden';\nconst EVENT_SHOW$2 = 'show';\nconst EVENT_SHOWN$2 = 'shown';\nconst EVENT_INSERTED = 'inserted';\nconst EVENT_CLICK$1 = 'click';\nconst EVENT_FOCUSIN$1 = 'focusin';\nconst EVENT_FOCUSOUT$1 = 'focusout';\nconst EVENT_MOUSEENTER = 'mouseenter';\nconst EVENT_MOUSELEAVE = 'mouseleave';\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n};\nconst Default$3 = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '
' + '
' + '
' + '
',\n title: '',\n trigger: 'hover focus'\n};\nconst DefaultType$3 = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n};\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n }\n super(element, config);\n\n // Private\n this._isEnabled = true;\n this._timeout = 0;\n this._isHovered = null;\n this._activeTrigger = {};\n this._popper = null;\n this._templateFactory = null;\n this._newContent = null;\n\n // Protected\n this.tip = null;\n this._setListeners();\n if (!this._config.selector) {\n this._fixTitle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$3;\n }\n static get DefaultType() {\n return DefaultType$3;\n }\n static get NAME() {\n return NAME$4;\n }\n\n // Public\n enable() {\n this._isEnabled = true;\n }\n disable() {\n this._isEnabled = false;\n }\n toggleEnabled() {\n this._isEnabled = !this._isEnabled;\n }\n toggle() {\n if (!this._isEnabled) {\n return;\n }\n this._activeTrigger.click = !this._activeTrigger.click;\n if (this._isShown()) {\n this._leave();\n return;\n }\n this._enter();\n }\n dispose() {\n clearTimeout(this._timeout);\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n }\n this._disposePopper();\n super.dispose();\n }\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements');\n }\n if (!(this._isWithContent() && this._isEnabled)) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2));\n const shadowRoot = findShadowRoot(this._element);\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n if (showEvent.defaultPrevented || !isInTheDom) {\n return;\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper();\n const tip = this._getTipElement();\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n const {\n container\n } = this._config;\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip);\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n }\n this._popper = this._createPopper(tip);\n tip.classList.add(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2));\n if (this._isHovered === false) {\n this._leave();\n }\n this._isHovered = false;\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n hide() {\n if (!this._isShown()) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2));\n if (hideEvent.defaultPrevented) {\n return;\n }\n const tip = this._getTipElement();\n tip.classList.remove(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n this._activeTrigger[TRIGGER_CLICK] = false;\n this._activeTrigger[TRIGGER_FOCUS] = false;\n this._activeTrigger[TRIGGER_HOVER] = false;\n this._isHovered = null; // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return;\n }\n if (!this._isHovered) {\n this._disposePopper();\n }\n this._element.removeAttribute('aria-describedby');\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2));\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n update() {\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle());\n }\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n }\n return this.tip;\n }\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml();\n\n // TODO: remove this check in v6\n if (!tip) {\n return null;\n }\n tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2);\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n const tipId = getUID(this.constructor.NAME).toString();\n tip.setAttribute('id', tipId);\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE$2);\n }\n return tip;\n }\n setContent(content) {\n this._newContent = content;\n if (this._isShown()) {\n this._disposePopper();\n this.show();\n }\n }\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content);\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n });\n }\n return this._templateFactory;\n }\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n };\n }\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n }\n _isAnimated() {\n return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2);\n }\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2);\n }\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element]);\n const attachment = AttachmentMap[placement.toUpperCase()];\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment));\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element]);\n }\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [{\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }, {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n }, {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n }\n }]\n };\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _setListeners() {\n const triggers = this._config.trigger.split(' ');\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context.toggle();\n });\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1);\n const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1);\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n context._enter();\n });\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n context._leave();\n });\n }\n }\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide();\n }\n };\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n }\n _fixTitle() {\n const title = this._element.getAttribute('title');\n if (!title) {\n return;\n }\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title);\n }\n this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title');\n }\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true;\n return;\n }\n this._isHovered = true;\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show();\n }\n }, this._config.delay.show);\n }\n _leave() {\n if (this._isWithActiveTrigger()) {\n return;\n }\n this._isHovered = false;\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide();\n }\n }, this._config.delay.hide);\n }\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout);\n this._timeout = setTimeout(handler, timeout);\n }\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true);\n }\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element);\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute];\n }\n }\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n };\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container);\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n };\n }\n if (typeof config.title === 'number') {\n config.title = config.title.toString();\n }\n if (typeof config.content === 'number') {\n config.content = config.content.toString();\n }\n return config;\n }\n _getDelegateConfig() {\n const config = {};\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value;\n }\n }\n config.selector = false;\n config.trigger = 'manual';\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config;\n }\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy();\n this._popper = null;\n }\n if (this.tip) {\n this.tip.remove();\n this.tip = null;\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$3 = 'popover';\nconst SELECTOR_TITLE = '.popover-header';\nconst SELECTOR_CONTENT = '.popover-body';\nconst Default$2 = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '
' + '
' + '

' + '
' + '
',\n trigger: 'click'\n};\nconst DefaultType$2 = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n};\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default$2;\n }\n static get DefaultType() {\n return DefaultType$2;\n }\n static get NAME() {\n return NAME$3;\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent();\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n };\n }\n _getContent() {\n return this._resolvePossibleFunction(this._config.content);\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$2 = 'scrollspy';\nconst DATA_KEY$2 = 'bs.scrollspy';\nconst EVENT_KEY$2 = `.${DATA_KEY$2}`;\nconst DATA_API_KEY = '.data-api';\nconst EVENT_ACTIVATE = `activate${EVENT_KEY$2}`;\nconst EVENT_CLICK = `click${EVENT_KEY$2}`;\nconst EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`;\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\nconst CLASS_NAME_ACTIVE$1 = 'active';\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\nconst SELECTOR_TARGET_LINKS = '[href]';\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\nconst SELECTOR_NAV_LINKS = '.nav-link';\nconst SELECTOR_NAV_ITEMS = '.nav-item';\nconst SELECTOR_LIST_ITEMS = '.list-group-item';\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\nconst SELECTOR_DROPDOWN = '.dropdown';\nconst SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';\nconst Default$1 = {\n offset: null,\n // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n};\nconst DefaultType$1 = {\n offset: '(number|null)',\n // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n};\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map();\n this._observableSections = new Map();\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n this._activeTarget = null;\n this._observer = null;\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n };\n this.refresh(); // initialize\n }\n\n // Getters\n static get Default() {\n return Default$1;\n }\n static get DefaultType() {\n return DefaultType$1;\n }\n static get NAME() {\n return NAME$2;\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables();\n this._maybeEnableSmoothScroll();\n if (this._observer) {\n this._observer.disconnect();\n } else {\n this._observer = this._getNewObserver();\n }\n for (const section of this._observableSections.values()) {\n this._observer.observe(section);\n }\n }\n dispose() {\n this._observer.disconnect();\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body;\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n }\n return config;\n }\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return;\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK);\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash);\n if (observableSection) {\n event.preventDefault();\n const root = this._rootElement || window;\n const height = observableSection.offsetTop - this._element.offsetTop;\n if (root.scrollTo) {\n root.scrollTo({\n top: height,\n behavior: 'smooth'\n });\n return;\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height;\n }\n });\n }\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n };\n return new IntersectionObserver(entries => this._observerCallback(entries), options);\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n this._process(targetElement(entry));\n };\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n this._previousScrollData.parentScrollTop = parentScrollTop;\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null;\n this._clearActiveClass(targetElement(entry));\n continue;\n }\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop;\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry);\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return;\n }\n continue;\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry);\n }\n }\n }\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map();\n this._observableSections = new Map();\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue;\n }\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element);\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor);\n this._observableSections.set(anchor.hash, observableSection);\n }\n }\n }\n _process(target) {\n if (this._activeTarget === target) {\n return;\n }\n this._clearActiveClass(this._config.target);\n this._activeTarget = target;\n target.classList.add(CLASS_NAME_ACTIVE$1);\n this._activateParents(target);\n EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n relatedTarget: target\n });\n }\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1);\n return;\n }\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both
    and