diff --git a/stable/0.4/.buildinfo b/stable/0.4/.buildinfo new file mode 100644 index 000000000..ed8a4bb8b --- /dev/null +++ b/stable/0.4/.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: 29bf4422ba7aa7a4a1c44bc977304a2e +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/stable/0.4/.doctrees/apidocs/array.doctree b/stable/0.4/.doctrees/apidocs/array.doctree new file mode 100644 index 000000000..ea4fa1e64 Binary files /dev/null and b/stable/0.4/.doctrees/apidocs/array.doctree differ diff --git a/stable/0.4/.doctrees/apidocs/backend.doctree b/stable/0.4/.doctrees/apidocs/backend.doctree new file mode 100644 index 000000000..a37588e28 Binary files /dev/null and b/stable/0.4/.doctrees/apidocs/backend.doctree differ diff --git a/stable/0.4/.doctrees/apidocs/dispatch.doctree b/stable/0.4/.doctrees/apidocs/dispatch.doctree new file mode 100644 index 000000000..08656b7bc Binary files /dev/null and b/stable/0.4/.doctrees/apidocs/dispatch.doctree differ diff --git a/stable/0.4/.doctrees/apidocs/index.doctree b/stable/0.4/.doctrees/apidocs/index.doctree new file mode 100644 index 000000000..0c6b41566 Binary files /dev/null and b/stable/0.4/.doctrees/apidocs/index.doctree differ diff --git a/stable/0.4/.doctrees/apidocs/models.doctree b/stable/0.4/.doctrees/apidocs/models.doctree new file mode 100644 index 000000000..5b30ebb95 Binary files /dev/null and b/stable/0.4/.doctrees/apidocs/models.doctree differ diff --git a/stable/0.4/.doctrees/apidocs/perturbation.doctree b/stable/0.4/.doctrees/apidocs/perturbation.doctree new file mode 100644 index 000000000..566997aff Binary files /dev/null and b/stable/0.4/.doctrees/apidocs/perturbation.doctree differ diff --git a/stable/0.4/.doctrees/apidocs/pulse.doctree b/stable/0.4/.doctrees/apidocs/pulse.doctree new file mode 100644 index 000000000..23b8fcb53 Binary files /dev/null and b/stable/0.4/.doctrees/apidocs/pulse.doctree differ diff --git a/stable/0.4/.doctrees/apidocs/signals.doctree b/stable/0.4/.doctrees/apidocs/signals.doctree new file mode 100644 index 000000000..bd5e37514 Binary files /dev/null and b/stable/0.4/.doctrees/apidocs/signals.doctree differ diff --git a/stable/0.4/.doctrees/apidocs/solvers.doctree b/stable/0.4/.doctrees/apidocs/solvers.doctree new file mode 100644 index 000000000..6e250482b Binary files /dev/null and b/stable/0.4/.doctrees/apidocs/solvers.doctree differ diff --git a/stable/0.4/.doctrees/discussions/dyson_magnus.doctree b/stable/0.4/.doctrees/discussions/dyson_magnus.doctree new file mode 100644 index 000000000..2d32d13aa Binary files /dev/null and b/stable/0.4/.doctrees/discussions/dyson_magnus.doctree differ diff --git a/stable/0.4/.doctrees/discussions/index.doctree b/stable/0.4/.doctrees/discussions/index.doctree new file mode 100644 index 000000000..3f142622d Binary files /dev/null and b/stable/0.4/.doctrees/discussions/index.doctree differ diff --git a/stable/0.4/.doctrees/environment.pickle b/stable/0.4/.doctrees/environment.pickle new file mode 100644 index 000000000..02e9350c4 Binary files /dev/null and b/stable/0.4/.doctrees/environment.pickle differ diff --git a/stable/0.4/.doctrees/index.doctree b/stable/0.4/.doctrees/index.doctree new file mode 100644 index 000000000..7b9a8a4dc Binary files /dev/null and b/stable/0.4/.doctrees/index.doctree differ diff --git a/stable/0.4/.doctrees/release_notes.doctree b/stable/0.4/.doctrees/release_notes.doctree new file mode 100644 index 000000000..432cfeb95 Binary files /dev/null and b/stable/0.4/.doctrees/release_notes.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.array.Array.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.array.Array.doctree new file mode 100644 index 000000000..c54fc7d5d Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.array.Array.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.array.wrap.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.array.wrap.doctree new file mode 100644 index 000000000..d6c459632 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.array.wrap.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.backend.DynamicsBackend.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.backend.DynamicsBackend.doctree new file mode 100644 index 000000000..0ae2f2c99 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.backend.DynamicsBackend.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.backend.default_experiment_result_function.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.backend.default_experiment_result_function.doctree new file mode 100644 index 000000000..5a505a38e Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.backend.default_experiment_result_function.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.backend.parse_backend_hamiltonian_dict.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.backend.parse_backend_hamiltonian_dict.doctree new file mode 100644 index 000000000..69667d237 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.backend.parse_backend_hamiltonian_dict.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.dispatch.asarray.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.dispatch.asarray.doctree new file mode 100644 index 000000000..b716f1084 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.dispatch.asarray.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.dispatch.requires_backend.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.dispatch.requires_backend.doctree new file mode 100644 index 000000000..fead179fd Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.dispatch.requires_backend.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.GeneratorModel.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.GeneratorModel.doctree new file mode 100644 index 000000000..90cabc146 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.GeneratorModel.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.HamiltonianModel.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.HamiltonianModel.doctree new file mode 100644 index 000000000..73309d604 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.HamiltonianModel.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.LindbladModel.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.LindbladModel.doctree new file mode 100644 index 000000000..85ff21a7b Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.LindbladModel.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.RotatingFrame.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.RotatingFrame.doctree new file mode 100644 index 000000000..67021d3ae Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.RotatingFrame.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.rotating_wave_approximation.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.rotating_wave_approximation.doctree new file mode 100644 index 000000000..7d0f8c1f0 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.models.rotating_wave_approximation.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.ArrayPolynomial.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.ArrayPolynomial.doctree new file mode 100644 index 000000000..e37ecf704 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.ArrayPolynomial.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.DysonLikeData.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.DysonLikeData.doctree new file mode 100644 index 000000000..0a55d2cdd Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.DysonLikeData.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.PowerSeriesData.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.PowerSeriesData.doctree new file mode 100644 index 000000000..2aaf5402b Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.PowerSeriesData.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.solve_lmde_perturbation.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.solve_lmde_perturbation.doctree new file mode 100644 index 000000000..46889f999 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.perturbation.solve_lmde_perturbation.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.pulse.InstructionToSignals.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.pulse.InstructionToSignals.doctree new file mode 100644 index 000000000..95cdaf00e Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.pulse.InstructionToSignals.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.Convolution.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.Convolution.doctree new file mode 100644 index 000000000..f18aaf420 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.Convolution.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.DiscreteSignal.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.DiscreteSignal.doctree new file mode 100644 index 000000000..4db0a0723 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.DiscreteSignal.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.DiscreteSignalSum.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.DiscreteSignalSum.doctree new file mode 100644 index 000000000..264e5299c Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.DiscreteSignalSum.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.IQMixer.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.IQMixer.doctree new file mode 100644 index 000000000..119c770f5 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.IQMixer.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.Signal.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.Signal.doctree new file mode 100644 index 000000000..9238fdc24 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.Signal.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.SignalList.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.SignalList.doctree new file mode 100644 index 000000000..f22adc333 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.SignalList.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.SignalSum.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.SignalSum.doctree new file mode 100644 index 000000000..8ca5616c1 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.signals.SignalSum.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.DysonSolver.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.DysonSolver.doctree new file mode 100644 index 000000000..ba688cc5e Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.DysonSolver.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.MagnusSolver.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.MagnusSolver.doctree new file mode 100644 index 000000000..531d7e33c Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.MagnusSolver.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.Solver.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.Solver.doctree new file mode 100644 index 000000000..fec7f5f93 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.Solver.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.solve_lmde.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.solve_lmde.doctree new file mode 100644 index 000000000..28ace71d9 Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.solve_lmde.doctree differ diff --git a/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.solve_ode.doctree b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.solve_ode.doctree new file mode 100644 index 000000000..6386903ed Binary files /dev/null and b/stable/0.4/.doctrees/stubs/qiskit_dynamics.solvers.solve_ode.doctree differ diff --git a/stable/0.4/.doctrees/tutorials/Lindblad_dynamics_simulation.doctree b/stable/0.4/.doctrees/tutorials/Lindblad_dynamics_simulation.doctree new file mode 100644 index 000000000..ebf97e3ca Binary files /dev/null and b/stable/0.4/.doctrees/tutorials/Lindblad_dynamics_simulation.doctree differ diff --git a/stable/0.4/.doctrees/tutorials/Rabi_oscillations.doctree b/stable/0.4/.doctrees/tutorials/Rabi_oscillations.doctree new file mode 100644 index 000000000..780e54718 Binary files /dev/null and b/stable/0.4/.doctrees/tutorials/Rabi_oscillations.doctree differ diff --git a/stable/0.4/.doctrees/tutorials/dynamics_backend.doctree b/stable/0.4/.doctrees/tutorials/dynamics_backend.doctree new file mode 100644 index 000000000..d762ff877 Binary files /dev/null and b/stable/0.4/.doctrees/tutorials/dynamics_backend.doctree differ diff --git a/stable/0.4/.doctrees/tutorials/index.doctree b/stable/0.4/.doctrees/tutorials/index.doctree new file mode 100644 index 000000000..286a09f85 Binary files /dev/null and b/stable/0.4/.doctrees/tutorials/index.doctree differ diff --git a/stable/0.4/.doctrees/tutorials/optimizing_pulse_sequence.doctree b/stable/0.4/.doctrees/tutorials/optimizing_pulse_sequence.doctree new file mode 100644 index 000000000..cf06a9f52 Binary files /dev/null and b/stable/0.4/.doctrees/tutorials/optimizing_pulse_sequence.doctree differ diff --git a/stable/0.4/.doctrees/tutorials/qiskit_pulse.doctree b/stable/0.4/.doctrees/tutorials/qiskit_pulse.doctree new file mode 100644 index 000000000..1518b320c Binary files /dev/null and b/stable/0.4/.doctrees/tutorials/qiskit_pulse.doctree differ diff --git a/stable/0.4/.doctrees/userguide/how_to_configure_simulations.doctree b/stable/0.4/.doctrees/userguide/how_to_configure_simulations.doctree new file mode 100644 index 000000000..36f0372c6 Binary files /dev/null and b/stable/0.4/.doctrees/userguide/how_to_configure_simulations.doctree differ diff --git a/stable/0.4/.doctrees/userguide/how_to_use_jax.doctree b/stable/0.4/.doctrees/userguide/how_to_use_jax.doctree new file mode 100644 index 000000000..2c8100d86 Binary files /dev/null and b/stable/0.4/.doctrees/userguide/how_to_use_jax.doctree differ diff --git a/stable/0.4/.doctrees/userguide/how_to_use_pulse_schedule_for_jax_jit.doctree b/stable/0.4/.doctrees/userguide/how_to_use_pulse_schedule_for_jax_jit.doctree new file mode 100644 index 000000000..60154d320 Binary files /dev/null and b/stable/0.4/.doctrees/userguide/how_to_use_pulse_schedule_for_jax_jit.doctree differ diff --git a/stable/0.4/.doctrees/userguide/index.doctree b/stable/0.4/.doctrees/userguide/index.doctree new file mode 100644 index 000000000..e2a59ecf2 Binary files /dev/null and b/stable/0.4/.doctrees/userguide/index.doctree differ diff --git a/stable/0.4/.doctrees/userguide/perturbative_solvers.doctree b/stable/0.4/.doctrees/userguide/perturbative_solvers.doctree new file mode 100644 index 000000000..f835620ee Binary files /dev/null and b/stable/0.4/.doctrees/userguide/perturbative_solvers.doctree differ diff --git a/stable/0.4/.nojekyll b/stable/0.4/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/stable/0.4/_images/Lindblad_dynamics_simulation_5_0.png b/stable/0.4/_images/Lindblad_dynamics_simulation_5_0.png new file mode 100644 index 000000000..fece76091 Binary files /dev/null and b/stable/0.4/_images/Lindblad_dynamics_simulation_5_0.png differ diff --git a/stable/0.4/_images/Lindblad_dynamics_simulation_5_1.png b/stable/0.4/_images/Lindblad_dynamics_simulation_5_1.png new file mode 100644 index 000000000..62d1c44ab Binary files /dev/null and b/stable/0.4/_images/Lindblad_dynamics_simulation_5_1.png differ diff --git a/stable/0.4/_images/Rabi_oscillations_3_0.png b/stable/0.4/_images/Rabi_oscillations_3_0.png new file mode 100644 index 000000000..c36eca688 Binary files /dev/null and b/stable/0.4/_images/Rabi_oscillations_3_0.png differ diff --git a/stable/0.4/_images/Rabi_oscillations_3_1.png b/stable/0.4/_images/Rabi_oscillations_3_1.png new file mode 100644 index 000000000..3141428c2 Binary files /dev/null and b/stable/0.4/_images/Rabi_oscillations_3_1.png differ diff --git a/stable/0.4/_images/Rabi_oscillations_4_0.png b/stable/0.4/_images/Rabi_oscillations_4_0.png new file mode 100644 index 000000000..e0e4ab0f6 Binary files /dev/null and b/stable/0.4/_images/Rabi_oscillations_4_0.png differ diff --git a/stable/0.4/_images/Rabi_oscillations_4_1.png b/stable/0.4/_images/Rabi_oscillations_4_1.png new file mode 100644 index 000000000..8584702b5 Binary files /dev/null and b/stable/0.4/_images/Rabi_oscillations_4_1.png differ diff --git a/stable/0.4/_images/dynamics_backend_18_0.png b/stable/0.4/_images/dynamics_backend_18_0.png new file mode 100644 index 000000000..99f2759f9 Binary files /dev/null and b/stable/0.4/_images/dynamics_backend_18_0.png differ diff --git a/stable/0.4/_images/dynamics_backend_19_0.png b/stable/0.4/_images/dynamics_backend_19_0.png new file mode 100644 index 000000000..1ec4902ca Binary files /dev/null and b/stable/0.4/_images/dynamics_backend_19_0.png differ diff --git a/stable/0.4/_images/dynamics_backend_21_0.png b/stable/0.4/_images/dynamics_backend_21_0.png new file mode 100644 index 000000000..0ba14e856 Binary files /dev/null and b/stable/0.4/_images/dynamics_backend_21_0.png differ diff --git a/stable/0.4/_images/dynamics_backend_23_0.png b/stable/0.4/_images/dynamics_backend_23_0.png new file mode 100644 index 000000000..2deedcb05 Binary files /dev/null and b/stable/0.4/_images/dynamics_backend_23_0.png differ diff --git a/stable/0.4/_images/dynamics_backend_24_0.png b/stable/0.4/_images/dynamics_backend_24_0.png new file mode 100644 index 000000000..7262fda9c Binary files /dev/null and b/stable/0.4/_images/dynamics_backend_24_0.png differ diff --git a/stable/0.4/_images/dynamics_backend_28_0.png b/stable/0.4/_images/dynamics_backend_28_0.png new file mode 100644 index 000000000..19f78bc14 Binary files /dev/null and b/stable/0.4/_images/dynamics_backend_28_0.png differ diff --git a/stable/0.4/_images/dynamics_backend_30_0.png b/stable/0.4/_images/dynamics_backend_30_0.png new file mode 100644 index 000000000..2e674a696 Binary files /dev/null and b/stable/0.4/_images/dynamics_backend_30_0.png differ diff --git a/stable/0.4/_images/dynamics_backend_6_0.png b/stable/0.4/_images/dynamics_backend_6_0.png new file mode 100644 index 000000000..408dd6680 Binary files /dev/null and b/stable/0.4/_images/dynamics_backend_6_0.png differ diff --git a/stable/0.4/_images/dynamics_backend_8_0.png b/stable/0.4/_images/dynamics_backend_8_0.png new file mode 100644 index 000000000..481eea9f8 Binary files /dev/null and b/stable/0.4/_images/dynamics_backend_8_0.png differ diff --git a/stable/0.4/_images/how_to_use_jax_8_0.png b/stable/0.4/_images/how_to_use_jax_8_0.png new file mode 100644 index 000000000..1a24a3f49 Binary files /dev/null and b/stable/0.4/_images/how_to_use_jax_8_0.png differ diff --git a/stable/0.4/_images/how_to_use_pulse_schedule_for_jax_jit_2_0.png b/stable/0.4/_images/how_to_use_pulse_schedule_for_jax_jit_2_0.png new file mode 100644 index 000000000..eb982bbf7 Binary files /dev/null and b/stable/0.4/_images/how_to_use_pulse_schedule_for_jax_jit_2_0.png differ diff --git a/stable/0.4/_images/optimizing_pulse_sequence_11_0.png b/stable/0.4/_images/optimizing_pulse_sequence_11_0.png new file mode 100644 index 000000000..c5ed7fb51 Binary files /dev/null and b/stable/0.4/_images/optimizing_pulse_sequence_11_0.png differ diff --git a/stable/0.4/_images/optimizing_pulse_sequence_13_0.png b/stable/0.4/_images/optimizing_pulse_sequence_13_0.png new file mode 100644 index 000000000..c37b8705d Binary files /dev/null and b/stable/0.4/_images/optimizing_pulse_sequence_13_0.png differ diff --git a/stable/0.4/_images/optimizing_pulse_sequence_3_0.png b/stable/0.4/_images/optimizing_pulse_sequence_3_0.png new file mode 100644 index 000000000..40e018886 Binary files /dev/null and b/stable/0.4/_images/optimizing_pulse_sequence_3_0.png differ diff --git a/stable/0.4/_images/optimizing_pulse_sequence_7_0.png b/stable/0.4/_images/optimizing_pulse_sequence_7_0.png new file mode 100644 index 000000000..cd291e08c Binary files /dev/null and b/stable/0.4/_images/optimizing_pulse_sequence_7_0.png differ diff --git a/stable/0.4/_images/pulse_0_0.png b/stable/0.4/_images/pulse_0_0.png new file mode 100644 index 000000000..aaf0d80b9 Binary files /dev/null and b/stable/0.4/_images/pulse_0_0.png differ diff --git a/stable/0.4/_images/qiskit_pulse_0_0.png b/stable/0.4/_images/qiskit_pulse_0_0.png new file mode 100644 index 000000000..e622c6581 Binary files /dev/null and b/stable/0.4/_images/qiskit_pulse_0_0.png differ diff --git a/stable/0.4/_images/qiskit_pulse_1_0.png b/stable/0.4/_images/qiskit_pulse_1_0.png new file mode 100644 index 000000000..7d19d05bc Binary files /dev/null and b/stable/0.4/_images/qiskit_pulse_1_0.png differ diff --git a/stable/0.4/_images/qiskit_pulse_5_0.png b/stable/0.4/_images/qiskit_pulse_5_0.png new file mode 100644 index 000000000..70190fc8a Binary files /dev/null and b/stable/0.4/_images/qiskit_pulse_5_0.png differ diff --git a/stable/0.4/_images/signals_0_0.png b/stable/0.4/_images/signals_0_0.png new file mode 100644 index 000000000..a3f12615a Binary files /dev/null and b/stable/0.4/_images/signals_0_0.png differ diff --git a/stable/0.4/_images/signals_1_0.png b/stable/0.4/_images/signals_1_0.png new file mode 100644 index 000000000..ab4244139 Binary files /dev/null and b/stable/0.4/_images/signals_1_0.png differ diff --git a/stable/0.4/_modules/index.html b/stable/0.4/_modules/index.html new file mode 100644 index 000000000..e4e7a8d84 --- /dev/null +++ b/stable/0.4/_modules/index.html @@ -0,0 +1,354 @@ + + + + + + + + Overview: module code - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+ + +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit/providers/backend.html b/stable/0.4/_modules/qiskit/providers/backend.html new file mode 100644 index 000000000..969b65d11 --- /dev/null +++ b/stable/0.4/_modules/qiskit/providers/backend.html @@ -0,0 +1,985 @@ + + + + + + + + qiskit.providers.backend - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit.providers.backend

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+# pylint: disable=invalid-name
+
+"""Backend abstract interface for providers."""
+
+
+from abc import ABC
+from abc import abstractmethod
+import datetime
+from typing import List, Union, Iterable, Tuple
+
+from qiskit.providers.provider import Provider
+from qiskit.providers.models.backendstatus import BackendStatus
+from qiskit.circuit.gate import Instruction
+
+
+class Backend:
+    """Base common type for all versioned Backend abstract classes.
+
+    Note this class should not be inherited from directly, it is intended
+    to be used for type checking. When implementing a provider you should use
+    the versioned abstract classes as the parent class and not this class
+    directly.
+    """
+
+    version = 0
+
+
+class BackendV1(Backend, ABC):
+    """Abstract class for Backends
+
+    This abstract class is to be used for all Backend objects created by a
+    provider. There are several classes of information contained in a Backend.
+    The first are the attributes of the class itself. These should be used to
+    defined the immutable characteristics of the backend. The ``options``
+    attribute of the backend is used to contain the dynamic user configurable
+    options of the backend. It should be used more for runtime options
+    that configure how the backend is used. For example, something like a
+    ``shots`` field for a backend that runs experiments which would contain an
+    int for how many shots to execute. The ``properties`` attribute is
+    optionally defined :class:`~qiskit.providers.models.BackendProperties`
+    object and is used to return measured properties, or properties
+    of a backend that may change over time. The simplest example of this would
+    be a version string, which will change as a backend is updated, but also
+    could be something like noise parameters for backends that run experiments.
+
+    This first version of the Backend abstract class is written to be mostly
+    backwards compatible with the legacy providers interface. This includes reusing
+    the model objects :class:`~qiskit.providers.models.BackendProperties` and
+    :class:`~qiskit.providers.models.BackendConfiguration`. This was done to
+    ease the transition for users and provider maintainers to the new versioned providers.
+    Expect, future versions of this abstract class to change the data model and
+    interface.
+
+    Subclasses of this should override the public method :meth:`run` and the internal
+    :meth:`_default_options`:
+
+    .. automethod:: _default_options
+    """
+
+    version = 1
+
+    def __init__(self, configuration, provider=None, **fields):
+        """Initialize a backend class
+
+        Args:
+            configuration (BackendConfiguration): A backend configuration
+                object for the backend object.
+            provider (qiskit.providers.Provider): Optionally, the provider
+                object that this Backend comes from.
+            fields: kwargs for the values to use to override the default
+                options.
+        Raises:
+            AttributeError: if input field not a valid options
+
+        ..
+            This next bit is necessary just because autosummary generally won't summarise private
+            methods; changing that behaviour would have annoying knock-on effects through all the
+            rest of the documentation, so instead we just hard-code the automethod directive.
+
+        In addition to the public abstract methods, subclasses should also implement the following
+        private methods:
+
+        .. automethod:: _default_options
+           :noindex:
+        """
+        self._configuration = configuration
+        self._options = self._default_options()
+        self._provider = provider
+        if fields:
+            for field in fields:
+                if field not in self._options.data:
+                    raise AttributeError("Options field %s is not valid for this backend" % field)
+            self._options.update_config(**fields)
+
+    @classmethod
+    @abstractmethod
+    def _default_options(cls):
+        """Return the default options
+
+        This method will return a :class:`qiskit.providers.Options`
+        subclass object that will be used for the default options. These
+        should be the default parameters to use for the options of the
+        backend.
+
+        Returns:
+            qiskit.providers.Options: A options object with
+                default values set
+        """
+
+    def set_options(self, **fields):
+        """Set the options fields for the backend
+
+        This method is used to update the options of a backend. If
+        you need to change any of the options prior to running just
+        pass in the kwarg with the new value for the options.
+
+        Args:
+            fields: The fields to update the options
+
+        Raises:
+            AttributeError: If the field passed in is not part of the
+                options
+        """
+        for field in fields:
+            if not hasattr(self._options, field):
+                raise AttributeError("Options field %s is not valid for this backend" % field)
+        self._options.update_options(**fields)
+
+    def configuration(self):
+        """Return the backend configuration.
+
+        Returns:
+            BackendConfiguration: the configuration for the backend.
+        """
+        return self._configuration
+
+    def properties(self):
+        """Return the backend properties.
+
+        Returns:
+            BackendProperties: the configuration for the backend. If the backend
+            does not support properties, it returns ``None``.
+        """
+        return None
+
+    def provider(self):
+        """Return the backend Provider.
+
+        Returns:
+            Provider: the Provider responsible for the backend.
+        """
+        return self._provider
+
+    def status(self):
+        """Return the backend status.
+
+        Returns:
+            BackendStatus: the status of the backend.
+        """
+        return BackendStatus(
+            backend_name=self.name(),
+            backend_version="1",
+            operational=True,
+            pending_jobs=0,
+            status_msg="",
+        )
+
+    def name(self):
+        """Return the backend name.
+
+        Returns:
+            str: the name of the backend.
+        """
+        return self._configuration.backend_name
+
+    def __str__(self):
+        return self.name()
+
+    def __repr__(self):
+        """Official string representation of a Backend.
+
+        Note that, by Qiskit convention, it is consciously *not* a fully valid
+        Python expression. Subclasses should provide 'a string of the form
+        <...some useful description...>'. [0]
+
+        [0] https://docs.python.org/3/reference/datamodel.html#object.__repr__
+        """
+        return f"<{self.__class__.__name__}('{self.name()}')>"
+
+    @property
+    def options(self):
+        """Return the options for the backend
+
+        The options of a backend are the dynamic parameters defining
+        how the backend is used. These are used to control the :meth:`run`
+        method.
+        """
+        return self._options
+
+    @abstractmethod
+    def run(self, run_input, **options):
+        """Run on the backend.
+
+        This method returns a :class:`~qiskit.providers.Job` object
+        that runs circuits. Depending on the backend this may be either an async
+        or sync call. It is at the discretion of the provider to decide whether
+        running should block until the execution is finished or not: the Job
+        class can handle either situation.
+
+        Args:
+            run_input (QuantumCircuit or Schedule or list): An individual or a
+                list of :class:`~qiskit.circuit.QuantumCircuit` or
+                :class:`~qiskit.pulse.Schedule` objects to run on the backend.
+                For legacy providers migrating to the new versioned providers,
+                provider interface a :class:`~qiskit.qobj.QasmQobj` or
+                :class:`~qiskit.qobj.PulseQobj` objects should probably be
+                supported too (but deprecated) for backwards compatibility. Be
+                sure to update the docstrings of subclasses implementing this
+                method to document that. New provider implementations should not
+                do this though as :mod:`qiskit.qobj` will be deprecated and
+                removed along with the legacy providers interface.
+            options: Any kwarg options to pass to the backend for running the
+                config. If a key is also present in the options
+                attribute/object then the expectation is that the value
+                specified will be used instead of what's set in the options
+                object.
+        Returns:
+            Job: The job object for the run
+        """
+        pass
+
+
+class QubitProperties:
+    """A representation of the properties of a qubit on a backend.
+
+    This class provides the optional properties that a backend can provide for
+    a qubit. These represent the set of qubit properties that Qiskit can
+    currently work with if present. However if your backend provides additional
+    properties of qubits you should subclass this to add additional custom
+    attributes for those custom/additional properties provided by the backend.
+    """
+
+    __slots__ = ("t1", "t2", "frequency")
+
+    def __init__(self, t1=None, t2=None, frequency=None):
+        """Create a new :class:`QubitProperties` object.
+
+        Args:
+            t1: The T1 time for a qubit in seconds
+            t2: The T2 time for a qubit in seconds
+            frequency: The frequency of a qubit in Hz
+        """
+        self.t1 = t1
+        self.t2 = t2
+        self.frequency = frequency
+
+    def __repr__(self):
+        return f"QubitProperties(t1={self.t1}, t2={self.t2}, " f"frequency={self.frequency})"
+
+
+class BackendV2(Backend, ABC):
+    """Abstract class for Backends
+
+    This abstract class is to be used for all Backend objects created by a
+    provider. This version differs from earlier abstract Backend classes in
+    that the configuration attribute no longer exists. Instead, attributes
+    exposing equivalent required immutable properties of the backend device
+    are added. For example ``backend.configuration().n_qubits`` is accessible
+    from ``backend.num_qubits`` now.
+
+    The ``options`` attribute of the backend is used to contain the dynamic
+    user configurable options of the backend. It should be used more for
+    runtime options that configure how the backend is used. For example,
+    something like a ``shots`` field for a backend that runs experiments which
+    would contain an int for how many shots to execute.
+
+    If migrating a provider from :class:`~qiskit.providers.BackendV1`
+    one thing to keep in mind is for
+    backwards compatibility you might need to add a configuration method that
+    will build a :class:`~qiskit.providers.models.BackendConfiguration` object
+    and :class:`~qiskit.providers.models.BackendProperties` from the attributes
+    defined in this class for backwards compatibility.
+
+    A backend object can optionally contain methods named
+    ``get_translation_stage_plugin`` and ``get_scheduling_stage_plugin``. If these
+    methods are present on a backend object and this object is used for
+    :func:`~.transpile` or :func:`~.generate_preset_pass_manager` the
+    transpilation process will default to using the output from those methods
+    as the scheduling stage and the translation compilation stage. This
+    enables a backend which has custom requirements for compilation to specify a
+    stage plugin for these stages to enable custom transformation of
+    the circuit to ensure it is runnable on the backend. These hooks are enabled
+    by default and should only be used to enable extra compilation steps
+    if they are **required** to ensure a circuit is executable on the backend or
+    have the expected level of performance. These methods are passed no input
+    arguments and are expected to return a ``str`` representing the method name
+    which should be a stage plugin (see: :mod:`qiskit.transpiler.preset_passmanagers.plugin`
+    for more details on plugins). The typical expected use case is for a backend
+    provider to implement a stage plugin for ``translation`` or ``scheduling``
+    that contains the custom compilation passes and then for the hook methods on
+    the backend object to return the plugin name so that :func:`~.transpile` will
+    use it by default when targetting the backend.
+
+    Subclasses of this should override the public method :meth:`run` and the internal
+    :meth:`_default_options`:
+
+    .. automethod:: _default_options
+    """
+
+    version = 2
+
+    def __init__(
+        self,
+        provider: Provider = None,
+        name: str = None,
+        description: str = None,
+        online_date: datetime.datetime = None,
+        backend_version: str = None,
+        **fields,
+    ):
+        """Initialize a BackendV2 based backend
+
+        Args:
+            provider: An optional backwards reference to the
+                :class:`~qiskit.providers.Provider` object that the backend
+                is from
+            name: An optional name for the backend
+            description: An optional description of the backend
+            online_date: An optional datetime the backend was brought online
+            backend_version: An optional backend version string. This differs
+                from the :attr:`~qiskit.providers.BackendV2.version` attribute
+                as :attr:`~qiskit.providers.BackendV2.version` is for the
+                abstract :attr:`~qiskit.providers.Backend` abstract interface
+                version of the object while ``backend_version`` is for
+                versioning the backend itself.
+            fields: kwargs for the values to use to override the default
+                options.
+
+        Raises:
+            AttributeError: If a field is specified that's outside the backend's
+                options
+        """
+
+        self._options = self._default_options()
+        self._provider = provider
+        if fields:
+            for field in fields:
+                if field not in self._options.data:
+                    raise AttributeError("Options field %s is not valid for this backend" % field)
+            self._options.update_config(**fields)
+        self.name = name
+        """Name of the backend."""
+        self.description = description
+        """Optional human-readable description."""
+        self.online_date = online_date
+        """Date that the backend came online."""
+        self.backend_version = backend_version
+        """Version of the backend being provided.  This is not the same as
+        :attr:`.BackendV2.version`, which is the version of the :class:`~.providers.Backend`
+        abstract interface."""
+        self._coupling_map = None
+
+    @property
+    def instructions(self) -> List[Tuple[Instruction, Tuple[int]]]:
+        """A list of Instruction tuples on the backend of the form ``(instruction, (qubits)``"""
+        return self.target.instructions
+
+    @property
+    def operations(self) -> List[Instruction]:
+        """A list of :class:`~qiskit.circuit.Instruction` instances that the backend supports."""
+        return list(self.target.operations)
+
+    @property
+    def operation_names(self) -> List[str]:
+        """A list of instruction names that the backend supports."""
+        return list(self.target.operation_names)
+
+    @property
+    @abstractmethod
+    def target(self):
+        """A :class:`qiskit.transpiler.Target` object for the backend.
+
+        :rtype: Target
+        """
+        pass
+
+    @property
+    def num_qubits(self) -> int:
+        """Return the number of qubits the backend has."""
+        return self.target.num_qubits
+
+    @property
+    def coupling_map(self):
+        """Return the :class:`~qiskit.transpiler.CouplingMap` object"""
+        if self._coupling_map is None:
+            self._coupling_map = self.target.build_coupling_map()
+        return self._coupling_map
+
+    @property
+    def instruction_durations(self):
+        """Return the :class:`~qiskit.transpiler.InstructionDurations` object."""
+        return self.target.durations()
+
+    @property
+    @abstractmethod
+    def max_circuits(self):
+        """The maximum number of circuits (or Pulse schedules) that can be
+        run in a single job.
+
+        If there is no limit this will return None
+        """
+        pass
+
+    @classmethod
+    @abstractmethod
+    def _default_options(cls):
+        """Return the default options
+
+        This method will return a :class:`qiskit.providers.Options`
+        subclass object that will be used for the default options. These
+        should be the default parameters to use for the options of the
+        backend.
+
+        Returns:
+            qiskit.providers.Options: A options object with
+                default values set
+        """
+        pass
+
+    @property
+    def dt(self) -> Union[float, None]:
+        """Return the system time resolution of input signals
+
+        This is required to be implemented if the backend supports Pulse
+        scheduling.
+
+        Returns:
+            The input signal timestep in seconds. If the backend doesn't define ``dt``, ``None`` will
+            be returned.
+        """
+        return self.target.dt
+
+    @property
+    def dtm(self) -> float:
+        """Return the system time resolution of output signals
+
+        Returns:
+            The output signal timestep in seconds.
+
+        Raises:
+            NotImplementedError: if the backend doesn't support querying the
+                output signal timestep
+        """
+        raise NotImplementedError
+
+    @property
+    def meas_map(self) -> List[List[int]]:
+        """Return the grouping of measurements which are multiplexed
+
+        This is required to be implemented if the backend supports Pulse
+        scheduling.
+
+        Returns:
+            The grouping of measurements which are multiplexed
+
+        Raises:
+            NotImplementedError: if the backend doesn't support querying the
+                measurement mapping
+        """
+        raise NotImplementedError
+
+    @property
+    def instruction_schedule_map(self):
+        """Return the :class:`~qiskit.pulse.InstructionScheduleMap` for the
+        instructions defined in this backend's target."""
+        return self.target.instruction_schedule_map()
+
+    def qubit_properties(
+        self, qubit: Union[int, List[int]]
+    ) -> Union[QubitProperties, List[QubitProperties]]:
+        """Return QubitProperties for a given qubit.
+
+        If there are no defined or the backend doesn't support querying these
+        details this method does not need to be implemented.
+
+        Args:
+            qubit: The qubit to get the
+                :class:`.QubitProperties` object for. This can
+                be a single integer for 1 qubit or a list of qubits and a list
+                of :class:`.QubitProperties` objects will be
+                returned in the same order
+        Returns:
+            The :class:`~.QubitProperties` object for the
+            specified qubit. If a list of qubits is provided a list will be
+            returned. If properties are missing for a qubit this can be
+            ``None``.
+
+        Raises:
+            NotImplementedError: if the backend doesn't support querying the
+                qubit properties
+        """
+        # Since the target didn't always have a qubit properties attribute
+        # to ensure the behavior here is backwards compatible with earlier
+        # BacekendV2 implementations where this would raise a NotImplemented
+        # error.
+        if self.target.qubit_properties is None:
+            raise NotImplementedError
+        if isinstance(qubit, int):
+            return self.target.qubit_properties[qubit]
+        return [self.target.qubit_properties[q] for q in qubit]
+
+    def drive_channel(self, qubit: int):
+        """Return the drive channel for the given qubit.
+
+        This is required to be implemented if the backend supports Pulse
+        scheduling.
+
+        Returns:
+            DriveChannel: The Qubit drive channel
+
+        Raises:
+            NotImplementedError: if the backend doesn't support querying the
+                measurement mapping
+        """
+        raise NotImplementedError
+
+    def measure_channel(self, qubit: int):
+        """Return the measure stimulus channel for the given qubit.
+
+        This is required to be implemented if the backend supports Pulse
+        scheduling.
+
+        Returns:
+            MeasureChannel: The Qubit measurement stimulus line
+
+        Raises:
+            NotImplementedError: if the backend doesn't support querying the
+                measurement mapping
+        """
+        raise NotImplementedError
+
+    def acquire_channel(self, qubit: int):
+        """Return the acquisition channel for the given qubit.
+
+        This is required to be implemented if the backend supports Pulse
+        scheduling.
+
+        Returns:
+            AcquireChannel: The Qubit measurement acquisition line.
+
+        Raises:
+            NotImplementedError: if the backend doesn't support querying the
+                measurement mapping
+        """
+        raise NotImplementedError
+
+    def control_channel(self, qubits: Iterable[int]):
+        """Return the secondary drive channel for the given qubit
+
+        This is typically utilized for controlling multiqubit interactions.
+        This channel is derived from other channels.
+
+        This is required to be implemented if the backend supports Pulse
+        scheduling.
+
+        Args:
+            qubits: Tuple or list of qubits of the form
+                ``(control_qubit, target_qubit)``.
+
+        Returns:
+            List[ControlChannel]: The multi qubit control line.
+
+        Raises:
+            NotImplementedError: if the backend doesn't support querying the
+                measurement mapping
+        """
+        raise NotImplementedError
+
+    def set_options(self, **fields):
+        """Set the options fields for the backend
+
+        This method is used to update the options of a backend. If
+        you need to change any of the options prior to running just
+        pass in the kwarg with the new value for the options.
+
+        Args:
+            fields: The fields to update the options
+
+        Raises:
+            AttributeError: If the field passed in is not part of the
+                options
+        """
+        for field in fields:
+            if not hasattr(self._options, field):
+                raise AttributeError("Options field %s is not valid for this backend" % field)
+        self._options.update_options(**fields)
+
+    @property
+    def options(self):
+        """Return the options for the backend
+
+        The options of a backend are the dynamic parameters defining
+        how the backend is used. These are used to control the :meth:`run`
+        method.
+        """
+        return self._options
+
+    @property
+    def provider(self):
+        """Return the backend Provider.
+
+        Returns:
+            Provider: the Provider responsible for the backend.
+        """
+        return self._provider
+
+    @abstractmethod
+    def run(self, run_input, **options):
+        """Run on the backend.
+
+        This method returns a :class:`~qiskit.providers.Job` object
+        that runs circuits. Depending on the backend this may be either an async
+        or sync call. It is at the discretion of the provider to decide whether
+        running should block until the execution is finished or not: the Job
+        class can handle either situation.
+
+        Args:
+            run_input (QuantumCircuit or Schedule or ScheduleBlock or list): An
+                individual or a list of :class:`.QuantumCircuit`,
+                :class:`~qiskit.pulse.ScheduleBlock`, or :class:`~qiskit.pulse.Schedule` objects to
+                run on the backend.
+            options: Any kwarg options to pass to the backend for running the
+                config. If a key is also present in the options
+                attribute/object then the expectation is that the value
+                specified will be used instead of what's set in the options
+                object.
+
+        Returns:
+            Job: The job object for the run
+        """
+        pass
+
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/array/array.html b/stable/0.4/_modules/qiskit_dynamics/array/array.html new file mode 100644 index 000000000..f302e0719 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/array/array.html @@ -0,0 +1,647 @@ + + + + + + + + qiskit_dynamics.array.array - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.array.array

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Array Class"""
+
+import copy
+from functools import wraps
+from types import BuiltinMethodType, MethodType
+from typing import Any, Optional, Union, Tuple, Set
+from numbers import Number
+
+import numpy
+from numpy.lib.mixins import NDArrayOperatorsMixin
+
+from qiskit_dynamics.dispatch.dispatch import Dispatch, asarray
+
+__all__ = ["Array"]
+
+
+
+[docs] +class Array(NDArrayOperatorsMixin): + """Qiskit array class. + + This class provides a Numpy compatible wrapper to supported Python array libraries. Supported + backends are ``"numpy"`` and ``"jax"``. + """ + + def __init__( + self, + data: Any, + dtype: Optional[Any] = None, + order: Optional[str] = None, + backend: Optional[str] = None, + ): + """Initialize an :class:`Array` container. + + Args: + data: An ``array_like`` input. This can be an object of any type supported by the + registered :math:`asarray` method for the specified backend. + dtype: Optional. The dtype of the returned array. This value must be supported by the + specified array backend. + order: Optional. The array order. This value must be supported by the specified array + backend. + backend: A registered array backend name. If ``None`` the default array backend will + be used. + + Raises: + ValueError: If input cannot be converted to an :class:`Array`. + """ + + # Check if we can override setattr and + # set _data and _backend directly + if ( + isinstance(data, numpy.ndarray) + and _is_numpy_backend(backend) + and _is_equivalent_numpy_array(data, dtype, order) + ): + self.__dict__["_data"] = data + self.__dict__["_backend"] = "numpy" + return + + if hasattr(data, "__qiskit_array__"): + array = data.__qiskit_array__(dtype=dtype, backend=backend) + if not isinstance(array, Array): + raise ValueError("object __qiskit_array__ method is not producing an Array") + self._data = array._data + self._backend = array._backend + if dtype or order or (backend and backend != self._backend): + if backend is None: + backend = self._backend + else: + self._backend = backend + self._data = asarray(self._data, dtype=dtype, order=order, backend=backend) + return + + # Standard init + self._data = asarray(data, dtype=dtype, order=order, backend=backend) + self._backend = backend if backend else Dispatch.backend(self._data, subclass=True) + + @property + def data(self): + """The wrapped array data object.""" + return self._data + + @data.setter + def data(self, value): + self._data[:] = value + + @property + def backend(self): + """The backend of the wrapped array class.""" + return self._backend + + @backend.setter + def backend(self, value: str): + Dispatch.validate_backend(value) + self._data = asarray(self._data, backend=value) + self._backend = value + +
+[docs] + @classmethod + def set_default_backend(cls, backend: Union[str, None]): + """Set the default array backend.""" + if backend is not None: + Dispatch.validate_backend(backend) + Dispatch.DEFAULT_BACKEND = backend
+ + +
+[docs] + @classmethod + def default_backend(cls) -> str: + """Return the default array backend.""" + return Dispatch.DEFAULT_BACKEND
+ + +
+[docs] + @classmethod + def available_backends(cls) -> Set[str]: + """Return a tuple of available array backends.""" + return Dispatch.REGISTERED_BACKENDS
+ + + def __repr__(self): + prefix = "Array(" + if self._backend == Dispatch.DEFAULT_BACKEND: + suffix = ")" + else: + suffix = f"backend='{self._backend}')" + return Dispatch.repr(self.backend)(self._data, prefix=prefix, suffix=suffix) + + def __copy__(self): + """Return a shallow copy referencing the same wrapped data array""" + return Array(self._data, backend=self.backend) + + def __deepcopy__(self, memo=None): + """Return a deep copy with a copy of the wrapped data array""" + return Array(copy.deepcopy(self._data), backend=self.backend) + + def __iter__(self): + return iter(self._data) + + def __getstate__(self): + return {"_data": self._data, "_backend": self._backend} + + def __setstate__(self, state): + self._backend = state["_backend"] + self._data = state["_data"] + + def __getitem__(self, key: str) -> Any: + """Return value from wrapped array.""" + return self._data[key] + + def __setitem__(self, key: str, value: Any): + """Return value of wrapped array.""" + self._data[key] = value + + def __setattr__(self, name: str, value: Any): + """Set attribute of wrapped array.""" + if name in ("_data", "data", "_backend", "backend"): + super().__setattr__(name, value) + else: + setattr(self._data, name, value) + + def __getattr__(self, name: str) -> Any: + """Get attribute of wrapped array and convert to an :class:`Array`.""" + # Call attribute on inner array object + attr = getattr(self._data, name) + + # If attribute is a function wrap the return values + if isinstance(attr, (MethodType, BuiltinMethodType)): + + @wraps(attr) + def wrapped_method(*args, **kwargs): + return self._wrap(attr(*args, **kwargs)) + + return wrapped_method + + # If return object is a backend array wrap result + return self._wrap(attr) + + def __qiskit_array__(self, dtype=None, backend=None): + if (backend and backend != self.backend) or (dtype and dtype != self.data.dtype): + return Array(self.data, dtype=dtype, backend=backend) + return self + + def __array__(self, dtype=None) -> numpy.ndarray: + if isinstance(self._data, numpy.ndarray) and (dtype is None or dtype == self._data.dtype): + return self._data + return numpy.asarray(self._data, dtype=dtype) + + def __len__(self) -> int: + return len(self._data) + + def __str__(self) -> str: + return str(self._data) + + def __bool__(self) -> bool: + return bool(self._data) + + def __int__(self): + """Convert size 1 array to an int.""" + if numpy.size(self) != 1: + raise TypeError("only size-1 Arrays can be converted to Python scalars.") + return int(self._data) + + def __float__(self): + """Convert size 1 array to a float.""" + if numpy.size(self) != 1: + raise TypeError("only size-1 Arrays can be converted to Python scalars.") + return float(self._data) + + def __complex__(self): + """Convert size 1 array to a complex.""" + if numpy.size(self) != 1: + raise TypeError("only size-1 Arrays can be converted to Python scalars.") + return complex(self._data) + + @staticmethod + def _wrap(obj: Union[Any, Tuple[Any]], backend: Optional[str] = None) -> Union[Any, Tuple[Any]]: + """Wrap return array backend objects as :class:`Array` objects.""" + if isinstance(obj, tuple): + return tuple( + Array(x, backend=backend) if isinstance(x, Dispatch.REGISTERED_TYPES) else x + for x in obj + ) + if isinstance(obj, Dispatch.REGISTERED_TYPES): + return Array(obj, backend=backend) + return obj + + @classmethod + def _unwrap(cls, obj): + """Unwrap an Array or list of :class:`Array` objects.""" + if isinstance(obj, Array): + return obj._data + if isinstance(obj, tuple): + return tuple(cls._unwrap(i) for i in obj) + if isinstance(obj, list): + return list(cls._unwrap(i) for i in obj) + return obj + + def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): + """Dispatcher for NumPy ufuncs to support the wrapped array backend.""" + out = kwargs.get("out", tuple()) + + for i in inputs + out: + # Only support operations with instances of REGISTERED_TYPES. + # Use ArrayLike instead of type(self) for isinstance to + # allow subclasses that don't override __array_ufunc__ to + # handle ArrayLike objects. + if not isinstance(i, Dispatch.REGISTERED_TYPES + (Array, Number)): + return NotImplemented + + # Defer to the implementation of the ufunc on unwrapped values. + inputs = self._unwrap(inputs) + if out: + kwargs["out"] = self._unwrap(out) + + # Get implementation for backend + backend = self.backend + dispatch_func = Dispatch.array_ufunc(backend, ufunc, method) + if dispatch_func == NotImplemented: + return NotImplemented + result = dispatch_func(*inputs, **kwargs) + + # Not sure what this case from NumPy docs is? + if method == "at": + return None + + # Wrap array results back into Array objects + return self._wrap(result, backend=self.backend) + + def __array_function__(self, func, types, args, kwargs): + """Dispatcher for NumPy array function to support the wrapped :class:`Array` backend.""" + if not all(issubclass(t, (Array,) + Dispatch.REGISTERED_TYPES) for t in types): + return NotImplemented + + # Unwrap function Array arguments + args = self._unwrap(args) + out = kwargs.get("out", tuple()) + if out: + kwargs["out"] = self._unwrap(out) + + # Get implementation for backend + backend = self.backend + dispatch_func = Dispatch.array_function(backend, func) + if dispatch_func == NotImplemented: + return NotImplemented + result = dispatch_func(*args, **kwargs) + return self._wrap(result, backend=self.backend)
+ + + +def _is_numpy_backend(backend: Optional[str] = None): + return backend == "numpy" or (not backend and Dispatch.DEFAULT_BACKEND == "numpy") + + +def _is_equivalent_numpy_array(data: Any, dtype: Optional[Any] = None, order: Optional[str] = None): + return (not dtype or dtype == data.dtype) and ( + not order + or (order == "C" and data.flags["C_CONTIGUOUS"]) + or (order == "F" and data.flags["F_CONTIGUOUS"]) + ) +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/array/wrap.html b/stable/0.4/_modules/qiskit_dynamics/array/wrap.html new file mode 100644 index 000000000..b522f62e1 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/array/wrap.html @@ -0,0 +1,550 @@ + + + + + + + + qiskit_dynamics.array.wrap - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.array.wrap

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2017, 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Functions for working with :class:`Array` dispatch."""
+
+import functools
+from types import FunctionType
+from typing import Callable
+
+from .array import Array
+
+
+
+[docs] +def wrap( + func: Callable, wrap_return: bool = True, wrap_args: bool = True, decorator: bool = False +) -> Callable: + r"""Wrap an array backend function to work with :class:`Array`\s. + + Args: + func: A function to wrap. + wrap_return: If ``True`` convert results that are registered array backend types into + :class:`Array` objects. + wrap_args: If ``True`` also wrap function type args and kwargs of the wrapped function. + decorator: If ``True`` the wrapped decorator function ``func`` will also wrap the + decorated functions. + + Returns: + Callable: The wrapped function. + """ + if decorator: + return _wrap_decorator(func, wrap_return=wrap_return, wrap_args=wrap_args) + else: + return _wrap_function(func, wrap_return=wrap_return, wrap_args=wrap_args)
+ + + +def _wrap_array_function(func: Callable) -> Callable: + r"""Wrap a function to handle :class:`Array`\-like inputs and returns.""" + + @functools.wraps(func) + def wrapped_function(*args, **kwargs): + # Unwrap inputs + args = tuple( + x.__qiskit_array__().data if hasattr(x, "__qiskit_array__") else x for x in args + ) + kwargs = dict( + (key, val.__qiskit_array__().data) if hasattr(val, "__qiskit_array__") else (key, val) + for key, val in kwargs.items() + ) + + # Evaluate function with unwrapped inputs + result = func(*args, **kwargs) + + # Unwrap result + if isinstance(result, tuple): + result = tuple( + x.__qiskit_array__().data if hasattr(x, "__qiskit_array__") else x for x in result + ) + elif hasattr(result, "__qiskit_array__"): + result = result.__qiskit_array__().data + return result + + return wrapped_function + + +def _wrap_args(args): + """Return wrapped args.""" + return tuple(_wrap_array_function(x) if isinstance(x, FunctionType) else x for x in args) + + +def _wrap_kwargs(kwargs): + """Return wrapped kwargs.""" + return dict( + (key, _wrap_array_function(val)) if isinstance(val, FunctionType) else (key, val) + for key, val in kwargs.items() + ) + + +def _wrap_function(func: Callable, wrap_return: bool = True, wrap_args: bool = True) -> Callable: + r"""Wrap an array backend function to work with :class:`Array`\s. + + Args: + func: A function to wrap. + wrap_return: If ``True`` convert results that are registered array backend types into + :class:`Array` objects. + wrap_args: If ``True`` also wrap function type args and kwargs of the wrapped function. + + Returns: + Callable: The wrapped function. + """ + # pylint: disable = function-redefined + if wrap_return and wrap_args: + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + args = _wrap_args(args) + kwargs = _wrap_kwargs(kwargs) + result = _wrap_array_function(func)(*args, **kwargs) + return Array._wrap(result) + + return wrapped_func + + elif wrap_return and not wrap_args: + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + result = _wrap_array_function(func)(*args, **kwargs) + return Array._wrap(result) + + return wrapped_func + + elif not wrap_return and wrap_args: + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + args = _wrap_args(args) + kwargs = _wrap_kwargs(kwargs) + return _wrap_array_function(func)(*args, **kwargs) + + return wrapped_func + + else: + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + return _wrap_array_function(func)(*args, **kwargs) + + return wrapped_func + + +def _wrap_decorator(func: Callable, wrap_return: bool = True, wrap_args: bool = True) -> Callable: + r"""Wrap a function decorator to work with :class:`Array`\s. + + Args: + func: A function to wrap. + wrap_return: If ``True`` convert results that are registered array backend types into + :class:`Array` objects. + wrap_args: If ``True`` also wrap function type args and kwargs of the wrapped function. + + Returns: + Callable: The wrapped function. + """ + # pylint: disable = function-redefined + if wrap_return and wrap_args: + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + args = _wrap_args(args) + kwargs = _wrap_kwargs(kwargs) + decorated = _wrap_array_function(func)(*args, **kwargs) + + @functools.wraps(args[0]) + def wrapped_decorated(*f_args, **f_kwargs): + f_args = _wrap_args(f_args) + f_kwargs = _wrap_kwargs(f_kwargs) + result = _wrap_function(decorated)(*f_args, **f_kwargs) + return Array._wrap(result) + + return wrapped_decorated + + return wrapped_func + + if wrap_return and not wrap_args: + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + decorated = _wrap_array_function(func)(*args, **kwargs) + + @functools.wraps(args[0]) + def wrapped_decorated(*f_args, **f_kwargs): + result = _wrap_function(decorated)(*f_args, **f_kwargs) + return Array._wrap(result) + + return wrapped_decorated + + return wrapped_func + + if not wrap_return and wrap_args: + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + args = _wrap_args(args) + kwargs = _wrap_kwargs(kwargs) + decorated = _wrap_array_function(func)(*args, **kwargs) + + @functools.wraps(args[0]) + def wrapped_decorated(*f_args, **f_kwargs): + f_args = _wrap_args(f_args) + f_kwargs = _wrap_kwargs(f_kwargs) + return _wrap_function(decorated)(*f_args, **f_kwargs) + + return wrapped_decorated + + return wrapped_func + + else: + + @functools.wraps(func) + def wrapped_func(*args, **kwargs): + decorated = _wrap_array_function(func)(*args, **kwargs) + + @functools.wraps(args[0]) + def wrapped_decorated(*f_args, **f_kwargs): + return _wrap_function(decorated)(*f_args, **f_kwargs) + + return wrapped_decorated + + return wrapped_func +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/backend/backend_string_parser/hamiltonian_string_parser.html b/stable/0.4/_modules/qiskit_dynamics/backend/backend_string_parser/hamiltonian_string_parser.html new file mode 100644 index 000000000..2fb6d5f88 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/backend/backend_string_parser/hamiltonian_string_parser.html @@ -0,0 +1,645 @@ + + + + + + + + qiskit_dynamics.backend.backend_string_parser.hamiltonian_string_parser - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.backend.backend_string_parser.hamiltonian_string_parser

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2022.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Functionality for importing qiskit.pulse model string representation.
+
+This file is meant for internal use and may be changed at any point.
+"""
+
+from typing import Tuple, List, Optional
+from collections import OrderedDict
+
+# required for calls to exec
+# pylint: disable=unused-import
+import numpy as np
+
+from qiskit import QiskitError
+
+from .regex_parser import _regex_parser
+
+
+# valid channel characters
+CHANNEL_CHARS = ["U", "D", "M", "A", "u", "d", "m", "a"]
+
+
+
+[docs] +def parse_backend_hamiltonian_dict( + hamiltonian_dict: dict, subsystem_list: Optional[List[int]] = None +) -> Tuple[np.ndarray, np.ndarray, List[str], dict]: + r"""Convert Pulse backend Hamiltonian dictionary into concrete array format + with an ordered list of corresponding channels. + + The Pulse backend Hamiltonian dictionary, ``hamiltonian_dict``, must have the + following keys: + + * ``'h_str'``: List of Hamiltonian terms in string format (see below). + * ``'qub'``: Dictionary giving subsystem dimensions. Keys are subsystem labels, + values are their dimensions. + * ``'vars'``: Dictionary whose keys are the variables appearing in the terms in + the ``h_str`` list, and values being the numerical values of the variables. + + The optional argument ``subsystem_list`` specifies a subset of subsystems to keep when parsing. + If ``None``, all subsystems are kept. If ``subsystem_list`` is specified, then terms + including subsystems not in the list will be ignored. + + Entries in the list ``hamiltonian_dict['h_str']`` must be formatted as a product of + constants (either numerical constants or variables in ``hamiltonian_dict['vars'].keys()``) + with operators. Operators are indicated with a capital letters followed by an integer + indicating the subsystem the operator acts on. Accepted operator strings are: + + * ``'X'``: If the target subsystem is two dimensional, the + Pauli :math:`X` operator, and if greater than two dimensional, returns + :math:`a + a^\dagger`, where :math:`a` and :math:`a^\dagger` are the + annihiliation and creation operators, respectively. + * ``'Y'``: If the target subsystem is two dimensional, the + Pauli :math:`Y` operator, and if greater than two dimensional, returns + :math:`-i(a - a^\dagger)`, where :math:`a` and :math:`a^\dagger` are the + annihiliation and creation operators, respectively. + * ``'Z'``: If the target subsystem is two dimensional, the + Pauli :math:`Z` operator, and if greater than two dimensional, returns + :math:`I - 2 * N`, where :math:`N` is the number operator. + * ``'a'``, ``'A'``, or ``'Sm'``: If two dimensional, the sigma minus operator, and if greater, + generalizes to the operator. + * ``'C'``, or ``'Sp'``: If two dimensional, sigma plus operator, and if greater, + generalizes to the creation operator. + * ``'N'``, or ``'O'``: The number operator. + * ``'I'``: The identity operator. + + In addition to the above, a term in ``hamiltonian_dict['h_str']`` can be associated with + a channel by ending it with a string of the form ``'||Sxx'``, where ``S`` is a valid channel + label, and ``'xx'`` is an integer. Accepted channel labels are: + + * ``'D'`` or ``'d'`` for drive channels. + * ``'U'`` or ``'u'`` for control channels. + * ``'M'`` or ``'m'`` for measurement channels. + * ``'A'`` or ``'a'`` for acquire channels. + + Finally, summations of terms of the above form can be indicated in + ``hamiltonian_dict['h_str']`` via strings with syntax ``'_SUM[i, lb, ub, aa||S{i}]'``, + where: + + * ``i`` is the summation variable. + * ``lb`` and ``ub`` are the summation endpoints (inclusive). + * ``aa`` is a valid operator string, possibly including the string ``{i}`` to indicate + operators acting on subsystem ``i``. + * ``S{i}`` is the specification of a channel indexed by ``i``. + + + For example, the following ``hamiltonian_dict`` specifies a single + transmon with 4 levels: + + .. code-block:: python + + hamiltonian_dict = { + "h_str": ["v*np.pi*O0", "alpha*np.pi*O0*O0", "r*np.pi*X0||D0"], + "qub": {"0": 4}, + "vars": {"v": 2.1, "alpha": -0.33, "r": 0.02}, + } + + The following example specifies a two transmon system, with single system terms specified + using the summation format: + + .. code-block:: python + + hamiltonian_dict = { + "h_str": [ + "_SUM[i,0,1,wq{i}/2*(I{i}-Z{i})]", + "_SUM[i,0,1,delta{i}/2*O{i}*O{i}]", + "_SUM[i,0,1,-delta{i}/2*O{i}]", + "_SUM[i,0,1,omegad{i}*X{i}||D{i}]", + "jq0q1*Sp0*Sm1", + "jq0q1*Sm0*Sp1", + "omegad1*X0||U0", + "omegad0*X1||U1" + ], + "qub": {"0": 4, "1": 4}, + "vars": { + "delta0": -2.111793476400394, + "delta1": -2.0894421352015744, + "jq0q1": 0.010495754104003914, + "omegad0": 0.9715458990879812, + "omegad1": 0.9803812537440838, + "wq0": 32.517894442809514, + "wq1": 33.0948996120196, + }, + } + + Args: + hamiltonian_dict: Pulse backend Hamiltonian dictionary. + subsystem_list: List of subsystems to include in the model. If ``None`` all are kept. + + Returns: + Tuple: Model converted into concrete arrays - the static Hamiltonian, + a list of Hamiltonians corresponding to different channels, a list of + channel labels corresponding to the list of time-dependent Hamiltonians, + and a dictionary with subsystem dimensions whose keys are the subsystem labels. + """ + + # raise errors for invalid hamiltonian_dict + _hamiltonian_pre_parse_exceptions(hamiltonian_dict) + + # get variables + variables = OrderedDict() + if "vars" in hamiltonian_dict: + variables = OrderedDict(hamiltonian_dict["vars"]) + + # Get qubit subspace dimensions + if subsystem_list is None: + subsystem_list = [int(qubit) for qubit in hamiltonian_dict["qub"]] + else: + # if user supplied, make a copy and sort it + subsystem_list = sorted(subsystem_list) + + # force keys in hamiltonian['qub'] to be ints + qub_dict = {int(key): val for key, val in hamiltonian_dict["qub"].items()} + + subsystem_dims_dict = {int(qubit): qub_dict[int(qubit)] for qubit in subsystem_list} + + # Parse the Hamiltonian + system = _regex_parser( + operator_str=hamiltonian_dict["h_str"], + subsystem_dims_dict=subsystem_dims_dict, + subsystem_list=subsystem_list, + ) + + # Extract which channels are associated with which Hamiltonian terms. + # Assumes one channel appearing in each term appearing at the end. + channels = [] + for _, ham_str in system: + chan_idx = None + + for c in CHANNEL_CHARS: + # if c in ham_str, and all characters after are digits, treat + # as channel + if c in ham_str: + if all(a.isdigit() for a in ham_str[ham_str.index(c) + 1 :]): + chan_idx = ham_str.index(c) + break + + if chan_idx is None: + channels.append(None) + else: + channels.append(ham_str[chan_idx:]) + + # evaluate coefficients + local_vars = {chan: 1.0 for chan in set(channels) if chan is not None} + local_vars.update(variables) + + evaluated_ops = [] + for op, coeff in system: + # pylint: disable=exec-used + exec(f"evaluated_coeff = {coeff}", globals(), local_vars) + evaluated_ops.append(local_vars["evaluated_coeff"] * op) + + # merge terms based on channel + static_hamiltonian = None + hamiltonian_operators = [] + reduced_channels = [] + + for channel, op in zip(channels, evaluated_ops): + # if None, add it to the static hamiltonian + if channel is None: + if static_hamiltonian is None: + static_hamiltonian = op + else: + static_hamiltonian += op + else: + channel = channel.lower() + if channel in reduced_channels: + hamiltonian_operators[reduced_channels.index(channel)] += op + else: + hamiltonian_operators.append(op) + reduced_channels.append(channel) + + # sort channels/operators according to channel ordering + if len(reduced_channels) > 0: + reduced_channels, hamiltonian_operators = zip( + *sorted(zip(reduced_channels, hamiltonian_operators)) + ) + + return ( + static_hamiltonian, + list(hamiltonian_operators), + list(reduced_channels), + subsystem_dims_dict, + )
+ + + +def _hamiltonian_pre_parse_exceptions(hamiltonian_dict: dict): + """Raises exceptions for improperly formatted or unsupported elements of + hamiltonian dict specification. + + Parameters: + hamiltonian_dict: Dictionary specification of hamiltonian. + Returns: + Raises: + QiskitError: If some part of the Hamiltonian dictionary is unsupported or invalid. + """ + + ham_str = hamiltonian_dict.get("h_str", []) + if ham_str in ([], [""]): + raise QiskitError("Hamiltonian dict requires a non-empty 'h_str' entry.") + + if hamiltonian_dict.get("qub", {}) == {}: + raise QiskitError( + "Hamiltonian dict requires non-empty 'qub' entry with subsystem dimensions." + ) + + if hamiltonian_dict.get("osc", {}) != {}: + raise QiskitError("Oscillator-type systems are not supported.") + + # verify that if terms in h_str have the divider ||, then the channels are in the valid format + for term in hamiltonian_dict["h_str"]: + malformed_text = f"""Term '{term}' does not conform to required string format. + Channels may only be specified in the format + 'aa||Cxx', where 'aa' specifies an operator, + C is a valid channel character, + and 'xx' is a string of digits.""" + + # if two vertical bars used together, check if channels in correct format + if term.count("|") == 2 and term.count("||") == 1: + # get the string reserved for channel + channel_str = term[term.index("||") + 2 :] + + # if channel string is empty + if len(channel_str) == 0: + raise QiskitError(malformed_text) + + # if first entry in channel string isn't a valid channel character + if channel_str[0] not in CHANNEL_CHARS: + raise QiskitError(malformed_text) + + # Verify either that: all remaining characters are digits, or, + # if term starts with _SUM[ and ends with ], all remaining characters + # are either digits, or starts and ends with {} + if term[-1] == "]" and len(term) > 5 and term[:5] == "_SUM[": + # drop the closing ] + channel_str = channel_str[:-1] + + # if channel string doesn't contain anything other than channel character + if len(channel_str) == 1: + raise QiskitError(malformed_text) + + # if starts with opening bracket, verify that it ends with closing bracket + if channel_str[1] == "{": + if not channel_str[-1] == "}": + raise QiskitError(malformed_text) + # otherwise verify that the remainder of terms only contains digits + elif any(not c.isdigit() for c in channel_str[1:]): + raise QiskitError(malformed_text) + else: + # if channel string doesn't contain anything other than channel character + if len(channel_str) == 1: + raise QiskitError(malformed_text) + + if any(not c.isdigit() for c in channel_str[1:]): + raise QiskitError(malformed_text) + + # if bars present but not in correct format, raise error + elif term.count("|") != 0: + raise QiskitError(malformed_text) +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/backend/dynamics_backend.html b/stable/0.4/_modules/qiskit_dynamics/backend/dynamics_backend.html new file mode 100644 index 000000000..f2b0138eb --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/backend/dynamics_backend.html @@ -0,0 +1,1433 @@ + + + + + + + + qiskit_dynamics.backend.dynamics_backend - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.backend.dynamics_backend

+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2022.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Pulse-enabled simulator backend.
+"""
+
+import datetime
+import uuid
+import warnings
+
+from typing import List, Optional, Union, Dict, Tuple
+import copy
+import numpy as np
+from scipy.integrate._ivp.ivp import OdeResult  # pylint: disable=unused-import
+
+from qiskit import pulse
+from qiskit.qobj.utils import MeasLevel, MeasReturnType
+from qiskit.qobj.common import QobjHeader
+from qiskit.transpiler import Target, InstructionProperties
+from qiskit.circuit.library import Measure
+from qiskit.pulse import Schedule, ScheduleBlock
+from qiskit.pulse.transforms.canonicalization import block_to_schedule
+from qiskit.providers.options import Options
+from qiskit.providers.backend import BackendV1, BackendV2
+from qiskit.providers.models.pulsedefaults import PulseDefaults
+from qiskit.providers.models.backendconfiguration import PulseBackendConfiguration
+from qiskit.result import Result
+from qiskit.result.models import ExperimentResult, ExperimentResultData
+
+from qiskit import QiskitError, QuantumCircuit
+from qiskit import schedule as build_schedule
+from qiskit.quantum_info import Statevector, DensityMatrix
+
+from qiskit_dynamics import RotatingFrame
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.solvers.solver_classes import Solver
+
+from .dynamics_job import DynamicsJob
+from .backend_utils import (
+    _get_dressed_state_decomposition,
+    _get_lab_frame_static_hamiltonian,
+    _get_memory_slot_probabilities,
+    _sample_probability_dict,
+    _get_counts_from_samples,
+    _get_iq_data,
+)
+from .backend_string_parser import parse_backend_hamiltonian_dict
+
+
+
+[docs] +class DynamicsBackend(BackendV2): + r"""Pulse-level simulator backend. + + This class provides a :class:`~qiskit.providers.backend.BackendV2` interface wrapper around a + :class:`.Solver` instance setup to simulate pulse schedules. The backend can be configured to + take advantage of standard transpilation infrastructure to describe pulse-level simulations in + terms of :class:`~qiskit.circuit.QuantumCircuit`\s. Results are returned as + :class:`~qiskit.result.Result` instances. + + A minimal :class:`.DynamicsBackend` requires specifying only a :class:`.Solver` instance and a + list of subsystem dimensions, indicating the subsystem decomposition of the model in + :class:`.Solver`. For example, the following code builds a :class:`.DynamicsBackend` around a + :class:`.Solver` and indicates that the system specified by the :class:`.Solver` decomposes as + two ``3`` dimensional subsystems. + + .. code-block:: python + + backend = DynamicsBackend( + solver=solver, subsystem_dims=[3, 3] + ) + + Without further configuration, the above ``backend`` can be used to simulate either + :class:`~qiskit.pulse.Schedule` or :class:`~qiskit.pulse.ScheduleBlock` instances. + + Pulse-level simulations defined in terms of :class:`~qiskit.circuit.QuantumCircuit` instances + can also be performed if each gate in the circuit has a corresponding pulse-level definition, + either as an attached calibration, or as an instruction contained in ``backend.target``. + + Additionally, a :class:`.DynamicsBackend` can be instantiated from an existing backend using the + :meth:`.DynamicsBackend.from_backend` method, utilizing the additional ``subsystem_list`` + argument to specify which qubits to include in the model: + + .. code-block:: python + + backend = DynamicsBackend.from_backend(backend, subsystem_list=[0, 1]) + + + **Supported options** + + The behaviour of the backend can be configured via the following options. These can either be + passed as optional keyword arguments at construction, set with the + :meth:`.DynamicsBackend.set_options` method after construction, or passed as runtime arguments + to :meth:`.DynamicsBackend.run`. + + * ``shots``: Number of shots per experiment. Defaults to ``1024``. + * ``solver``: The Qiskit Dynamics :class:`.Solver` instance used for simulation. + * ``solver_options``: Dictionary containing optional kwargs for passing to :meth:`Solver.solve`, + indicating solver methods and options. Defaults to the empty dictionary ``{}``. + * ``subsystem_dims``: Dimensions of subsystems making up the system in ``solver``. Defaults to + ``[solver.model.dim]``. + * ``meas_map``: Measurement map. Defaults to ``[[idx] for idx in range(len(subsystem_dims))]``. + * ``control_channel_map``: A dictionary mapping control channel labels to indices, to be used + for control channel index lookup in the :meth:`DynamicsBackend.control_channel` method. + * ``initial_state``: Initial state for simulation, either the string ``"ground_state"``, + indicating that the ground state for the system Hamiltonian should be used, or an arbitrary + ``Statevector`` or ``DensityMatrix``. Defaults to ``"ground_state"``. + * ``normalize_states``: Boolean indicating whether to normalize states before computing outcome + probabilities, and normalize probablities before sampling. Defaults to ``True``. Setting to + ``False`` can result in errors if the solution tolerance results in probabilities with + significant numerical deviation from a proper probability distribution. + * ``meas_level``: Form of measurement output. Supported values are ``1`` and ``2``. ``1`` + returns IQ points and ``2`` returns counts. Defaults to ``meas_level == 2``. + * ``meas_return``: Level of measurement data to return. For ``meas_level = 1`` ``"single"`` + returns output from every shot. ``"avg"`` returns average over shots of measurement output. + Defaults to ``"avg"``. + * ``iq_centers``: Centers for IQ distribution when using ``meas_level==1`` results. Must have + type ``List[List[List[float, float]]]`` formatted as ``iq_centers[subsystem][level] = [I, + Q]``. If ``None``, the ``iq_centers`` are dynamically generated to be equally spaced points on + a unit circle with ground-state at ``(1, 0)``. The default is ``None``. + * ``iq_width``: Standard deviation of IQ distribution around the centers for ``meas_level==1``. + Must be a positive float. Defaults to ``0.2``. + * ``max_outcome_level``: For ``meas_level == 2``, the maximum outcome for each subsystem. Values + will be rounded down to be no larger than ``max_outcome_level``. Must be a positive integer or + ``None``. If ``None``, no rounding occurs. Defaults to ``1``. + * ``memory``: Boolean indicating whether to return a list of explicit measurement outcomes for + every experimental shot. Defaults to ``True``. + * ``seed_simulator``: Seed to use in random sampling. Defaults to ``None``. + * ``experiment_result_function``: Function for computing the ``ExperimentResult`` for each + simulated experiment. This option defaults to :func:`default_experiment_result_function`, and + any other function set to this option must have the same signature. Note that the default + utilizes various other options that control results computation, and hence changing it will + impact the meaning of other options. + * ``configuration``: A :class:`PulseBackendConfiguration` instance or ``None``. This option + defaults to ``None``, and is not required for the functioning of this class, but is provided + for compatibility. A set configuration will be returned by + :meth:`DynamicsBackend.configuration()`. + * ``defaults``: A :class:`PulseDefaults` instance or ``None``. This option defaults to ``None``, + and is not required for the functioning of this class, but is provided for compatibility. A + set defaults will be returned by :meth:`DynamicsBackend.defaults()`. + """ + + def __init__( + self, + solver: Solver, + target: Optional[Target] = None, + **options, + ): + """Instantiate with a :class:`.Solver` instance and additional options. + + Args: + solver: Solver instance configured for pulse simulation. + target: Target object. + options: Additional configuration options for the simulator. + + Raises: + QiskitError: If any instantiation arguments fail validation checks. + """ + + super().__init__( + name="DynamicsBackend", + description="Pulse enabled simulator backend.", + backend_version="0.1", + ) + + # Dressed states of solver, will be calculated when solver option is set + self._dressed_evals = None + self._dressed_states = None + self._dressed_states_adjoint = None + + # add subsystem_dims to options so set_options validation works + if "subsystem_dims" not in options: + options["subsystem_dims"] = [solver.model.dim] + + # Set simulator options + self.set_options(solver=solver, **options) + + if self.options.meas_map is None: + meas_map = [[idx] for idx in range(len(self.options.subsystem_dims))] + self.set_options(meas_map=meas_map) + + # self._target = target or Target() doesn't work as bool(target) can be False + if target is None: + target = Target() + else: + target = copy.copy(target) + + # add default simulator measure instructions + measure_properties = {} + instruction_schedule_map = target.instruction_schedule_map() + for qubit in range(len(self.options.subsystem_dims)): + if not instruction_schedule_map.has(instruction="measure", qubits=qubit): + with pulse.build() as meas_sched: + pulse.acquire( + duration=1, qubit_or_channel=qubit, register=pulse.MemorySlot(qubit) + ) + + measure_properties[(qubit,)] = InstructionProperties(calibration=meas_sched) + + if bool(measure_properties): + target.add_instruction(Measure(), measure_properties) + + target.dt = solver._dt + target.num_qubits = len(self.options.subsystem_dims) + + self._target = target + + def _default_options(self): + return Options( + shots=1024, + solver=None, + solver_options={}, + subsystem_dims=None, + meas_map=None, + control_channel_map=None, + normalize_states=True, + initial_state="ground_state", + meas_level=MeasLevel.CLASSIFIED, + meas_return=MeasReturnType.AVERAGE, + iq_centers=None, + iq_width=0.2, + max_outcome_level=1, + memory=True, + seed_simulator=None, + experiment_result_function=default_experiment_result_function, + configuration=None, + defaults=None, + ) + +
+[docs] + def set_options(self, **fields): + """Set options for DynamicsBackend.""" + + validate_subsystem_dims = False + validate_iq_centers = False + + for key, value in fields.items(): + if not hasattr(self._options, key): + raise AttributeError(f"Invalid option {key}") + + # validation checks + if key == "initial_state": + if value != "ground_state" and not isinstance(value, (Statevector, DensityMatrix)): + raise QiskitError( + 'initial_state must be either "ground_state", or a Statevector or ' + "DensityMatrix instance." + ) + elif key == "meas_level" and value not in [1, 2]: + raise QiskitError("Only meas_level 1 and 2 are supported by DynamicsBackend.") + elif key == "meas_return" and value not in ["single", "avg"]: + raise QiskitError("meas_return must be either 'single' or 'avg'.") + elif key == "max_outcome_level": + if (value is not None) and (not isinstance(value, int) or (value <= 0)): + raise QiskitError("max_outcome_level must be a positive integer or None.") + elif key == "experiment_result_function" and not callable(value): + raise QiskitError("experiment_result_function must be callable.") + elif key == "configuration" and not isinstance(value, PulseBackendConfiguration): + raise QiskitError( + "configuration option must be an instance of PulseBackendConfiguration." + ) + elif key == "defaults" and not isinstance(value, PulseDefaults): + raise QiskitError("defaults option must be an instance of PulseDefaults.") + elif key == "iq_width" and (not isinstance(value, float) or (value <= 0)): + raise QiskitError("iq_width must be a positive float.") + elif key == "iq_centers": + if (value is not None) and not all( + (isinstance(level, List) and len(level) == 2) + for sub_system in value + for level in sub_system + ): + raise QiskitError( + "The iq_centers option must be either None or of type " + "List[List[List[int, int]]], where the innermost list is the (I, Q) pair." + ) + validate_iq_centers = True + elif key == "subsystem_dims": + validate_subsystem_dims = True + validate_iq_centers = True + elif key == "solver": + validate_subsystem_dims = True + elif key == "control_channel_map": + if value is not None: + if not isinstance(value, dict): + raise QiskitError( + "The control_channel_map option must either be None or a dictionary." + ) + if not all(isinstance(x, int) for x in value.values()): + raise QiskitError("The control_channel_map values must be of type int.") + + # special setting routines + if key == "solver": + self._set_solver(value) + else: + self._options.update_options(**{key: value}) + + # perform additional consistency validations if certain options were modified + if ( + validate_subsystem_dims + and np.prod(self._options.subsystem_dims) != self._options.solver.model.dim + ): + raise QiskitError( + "DynamicsBackend options subsystem_dims and solver.model.dim are inconsistent." + ) + + if validate_iq_centers and (self._options.iq_centers is not None): + if [ + len(sub_system) for sub_system in self._options.iq_centers + ] != self._options.subsystem_dims: + raise QiskitError( + """iq_centers option is not consistent with subsystem_dims. Must be None + or of type List[List[List[int, int]]], where the outermost list is of length equal + to the number of subsystems, and each inner list of length equal to the + corresponding subsystem dimension.""" + )
+ + + def _set_solver(self, solver): + """Configure simulator based on provided solver.""" + if solver._dt is None: + raise QiskitError( + "Solver passed to DynamicsBackend is not configured for Pulse simulation." + ) + + self._options.update_options(solver=solver) + # Get dressed states + static_hamiltonian = _get_lab_frame_static_hamiltonian(solver.model) + dressed_evals, dressed_states = _get_dressed_state_decomposition(static_hamiltonian) + self._dressed_evals = dressed_evals + self._dressed_states = dressed_states + self._dressed_states_adjoint = self._dressed_states.conj().transpose() + + # pylint: disable=arguments-differ +
+[docs] + def run( + self, + run_input: List[Union[QuantumCircuit, Schedule, ScheduleBlock]], + validate: Optional[bool] = True, + **options, + ) -> DynamicsJob: + """Run a list of simulations. + + Args: + run_input: A list of simulations, specified by ``QuantumCircuit``, ``Schedule``, or + ``ScheduleBlock`` instances. + validate: Whether or not to run validation checks on the input. + **options: Additional run options to temporarily override current backend options. + + Returns: + DynamicsJob object containing results and status. + + Raises: + QiskitError: If invalid options are set. + """ + + if validate: + _validate_run_input(run_input) + + # Configure run options for simulation + if options: + backend = copy.deepcopy(self) + backend.set_options(**options) + else: + backend = self + + schedules, num_memory_slots_list = _to_schedule_list(run_input, backend=backend) + + # get the acquires sample times and subsystem measurement information + ( + t_span, + measurement_subsystems_list, + memory_slot_indices_list, + ) = _get_acquire_instruction_timings( + schedules, backend.options.subsystem_dims, backend.options.solver._dt + ) + + # Build and submit job + job_id = str(uuid.uuid4()) + dynamics_job = DynamicsJob( + backend=backend, + job_id=job_id, + fn=backend._run, + fn_kwargs={ + "t_span": t_span, + "schedules": schedules, + "measurement_subsystems_list": measurement_subsystems_list, + "memory_slot_indices_list": memory_slot_indices_list, + "num_memory_slots_list": num_memory_slots_list, + }, + ) + dynamics_job.submit() + + return dynamics_job
+ + + def _run( + self, + job_id, + t_span, + schedules, + measurement_subsystems_list, + memory_slot_indices_list, + num_memory_slots_list, + ) -> Result: + """Simulate a list of schedules.""" + + # simulate all schedules + y0 = self.options.initial_state + if y0 == "ground_state": + y0 = Statevector(self._dressed_states[:, 0]) + + solver_results = self.options.solver.solve( + t_span=t_span, y0=y0, signals=schedules, **self.options.solver_options + ) + + # compute results for each experiment + experiment_names = [schedule.name for schedule in schedules] + experiment_metadatas = [schedule.metadata for schedule in schedules] + rng = np.random.default_rng(self.options.seed_simulator) + experiment_results = [] + for ( + experiment_name, + solver_result, + measurement_subsystems, + memory_slot_indices, + num_memory_slots, + experiment_metadata, + ) in zip( + experiment_names, + solver_results, + measurement_subsystems_list, + memory_slot_indices_list, + num_memory_slots_list, + experiment_metadatas, + ): + experiment_results.append( + self.options.experiment_result_function( + experiment_name, + solver_result, + measurement_subsystems, + memory_slot_indices, + num_memory_slots, + self, + seed=rng.integers(low=0, high=9223372036854775807), + metadata=experiment_metadata, + ) + ) + + # Construct full result object + return Result( + backend_name=self.name, + backend_version=self.backend_version, + qobj_id="", + job_id=job_id, + success=True, + results=experiment_results, + date=datetime.datetime.now().isoformat(), + ) + + @property + def max_circuits(self): + return None + + @property + def target(self) -> Target: + return self._target + + @property + def meas_map(self) -> List[List[int]]: + return self.options.meas_map + + def _get_qubit_channel( + self, qubit: int, ChannelClass: pulse.channels.Channel, method_name: str + ): + """Construct a channel instance for a given qubit.""" + if qubit < len(self.options.subsystem_dims): + return ChannelClass(qubit) + + raise QiskitError(f"{method_name} requested for qubit {qubit}, which is out of bounds.") + +
+[docs] + def drive_channel(self, qubit: int) -> pulse.DriveChannel: + """Return the drive channel for a given qubit.""" + return self._get_qubit_channel(qubit, pulse.DriveChannel, "drive_channel")
+ + +
+[docs] + def measure_channel(self, qubit: int) -> pulse.MeasureChannel: + """Return the measure channel for a given qubit.""" + return self._get_qubit_channel(qubit, pulse.MeasureChannel, "measure_channel")
+ + +
+[docs] + def acquire_channel(self, qubit: int) -> pulse.AcquireChannel: + """Return the measure channel for a given qubit.""" + return self._get_qubit_channel(qubit, pulse.AcquireChannel, "acquire_channel")
+ + +
+[docs] + def control_channel( + self, qubits: Union[Tuple[int, int], List[Tuple[int, int]]] + ) -> List[pulse.ControlChannel]: + """Return the control channel with a given label specified by qubits. + + This method requires the ``control_channel_map`` option is set, and otherwise will raise + a ``NotImplementedError``. + + Args: + qubits: The label for the control channel, or a list of labels. + Returns: + A list containing the control channels specified by qubits. + Raises: + NotImplementedError: If the control_channel_map option is not set for this backend. + QiskitError: If a requested channel is not in the control_channel_map. + """ + if self.options.control_channel_map is None: + raise NotImplementedError + + if not isinstance(qubits, list): + qubits = [qubits] + + control_channels = [] + for x in qubits: + if x not in self.options.control_channel_map: + raise QiskitError(f"Key {x} not in control_channel_map.") + control_channels.append(pulse.ControlChannel(self.options.control_channel_map[x])) + + return control_channels
+ + +
+[docs] + def configuration(self) -> PulseBackendConfiguration: + """Get the backend configuration.""" + return self.options.configuration
+ + +
+[docs] + def defaults(self) -> PulseDefaults: + """Get the backend defaults.""" + return self.options.defaults
+ + +
+[docs] + @classmethod + def from_backend( + cls, + backend: BackendV1, + subsystem_list: Optional[List[int]] = None, + rotating_frame: Optional[Union[Array, RotatingFrame, str]] = "auto", + evaluation_mode: str = "dense", + rwa_cutoff_freq: Optional[float] = None, + **options, + ) -> "DynamicsBackend": + """Construct a DynamicsBackend instance from an existing Backend instance. + + .. warning:: + + Due to inevitable model inaccuracies, gates calibrated on a real backend will not have + the same performance on the :class:`.DynamicsBackend` instance returned by this method. + As such, gates and calibrations are not be copied into the constructed + :class:`.DynamicsBackend`. + + The ``backend`` must contain sufficient information in the ``target``, ``configuration``, + and/or ``defaults`` attributes to be able to run simulations. The following table indicates + which parameters are required, along with their primary and secondary sources: + + .. list-table:: Backend parameter locations + :widths: 10 25 25 + :header-rows: 1 + + * - Parameter + - Primary source + - Secondary source + * - ``hamiltonian`` dictionary. + - ``configuration.hamiltonian`` + - N/A + * - Control channel frequency specification. + - ``configuration.u_channel_lo`` + - N/A + * - Number of qubits in the backend model. + - ``target.num_qubits`` + - ``configuration.n_qubits`` + * - Pulse schedule sample size ``dt``. + - ``target.dt`` + - ``configuration.dt`` + * - Drive channel frequencies. + - ``target.qubit_properties`` + - ``defaults.qubit_freq_est`` + * - Measurement channel frequencies, if measurement channels explicitly appear in the + model. + - ``defaults.meas_freq_est`` + - N/A + + .. note:: + + The ``target``, ``configuration``, and ``defaults`` attributes of the original backend + are not copied into the constructed :class:`DynamicsBackend` instance, only the required + data stored within these attributes will be extracted. If necessary, these attributes + can be set and configured by the user. + + The optional argument ``subsystem_list`` specifies which subset of qubits to model in the + constructed :class:`DynamicsBackend`. All other qubits are dropped from the model. + + Configuration of the underlying :class:`.Solver` is controlled via the ``rotating_frame``, + ``evaluation_mode``, and ``rwa_cutoff_freq`` options. In contrast to :class:`.Solver` + initialization, ``rotating_frame`` defaults to the string ``"auto"``, which allows this + method to choose the rotating frame based on ``evaluation_mode``: + + * If a dense evaluation mode is chosen, the rotating frame will be set to the + ``static_hamiltonian`` indicated by the Hamiltonian in ``backend.configuration()``. + * If a sparse evaluation mode is chosen, the rotating frame will be set to the diagonal of + ``static_hamiltonian``. + + Otherwise the ``rotating_frame``, ``evaluation_mode``, and ``rwa_cutoff_freq`` are passed + directly to the :class:`.Solver` initialization. + + Args: + backend: The ``Backend`` instance to build the :class:`.DynamicsBackend` from. + Note that while the type hint indicates that `backend` should be a + :class:`~qiskit.providers.backend.BackendV1` instance, this method also works for + :class:`~qiskit.providers.backend.BackendV2` instances that have been set up with + sufficiently populated ``configuration`` and ``defaults`` for backwards + compatibility. + subsystem_list: The list of qubits in the backend to include in the model. + rotating_frame: Rotating frame argument for the internal :class:`.Solver`. Defaults to + ``"auto"``, allowing this method to pick a rotating frame. + evaluation_mode: Evaluation mode argument for the internal :class:`.Solver`. + rwa_cutoff_freq: Rotating wave approximation argument for the internal :class:`.Solver`. + **options: Additional options to be applied in construction of the + :class:`.DynamicsBackend`. + + Returns: + DynamicsBackend + + Raises: + QiskitError: If any required parameters are missing from the passed backend. + """ + + # get available target, config, and defaults objects + backend_target = getattr(backend, "target", None) + + if not hasattr(backend, "configuration"): + raise QiskitError( + "DynamicsBackend.from_backend requires that the backend argument has a " + "configuration method." + ) + backend_config = backend.configuration() + + backend_defaults = None + if hasattr(backend, "defaults"): + backend_defaults = backend.defaults() + + # get and parse Hamiltonian string dictionary + if backend_target is not None: + backend_num_qubits = backend_target.num_qubits + else: + backend_num_qubits = backend_config.n_qubits + + if subsystem_list is not None: + subsystem_list = sorted(subsystem_list) + if subsystem_list[-1] >= backend_num_qubits: + raise QiskitError( + f"subsystem_list contained {subsystem_list[-1]}, which is out of bounds for " + f"backend with {backend_num_qubits} qubits." + ) + else: + subsystem_list = list(range(backend_num_qubits)) + + if backend_config.hamiltonian is None: + raise QiskitError( + "DynamicsBackend.from_backend requires that backend.configuration() has a " + "hamiltonian." + ) + + ( + static_hamiltonian, + hamiltonian_operators, + hamiltonian_channels, + subsystem_dims_dict, + ) = parse_backend_hamiltonian_dict(backend_config.hamiltonian, subsystem_list) + subsystem_dims = [subsystem_dims_dict.get(idx, 1) for idx in range(backend_num_qubits)] + + # construct model frequencies dictionary from backend + channel_freqs = _get_backend_channel_freqs( + backend_target=backend_target, + backend_config=backend_config, + backend_defaults=backend_defaults, + channels=hamiltonian_channels, + ) + + # Add control_channel_map from backend (only if not specified before by user) + if "control_channel_map" not in options: + if hasattr(backend, "control_channels"): + control_channel_map_backend = { + qubits: backend.control_channels[qubits][0].index + for qubits in backend.control_channels + } + + elif hasattr(backend.configuration(), "control_channels"): + control_channel_map_backend = { + qubits: backend.configuration().control_channels[qubits][0].index + for qubits in backend.configuration().control_channels + } + + else: + control_channel_map_backend = {} + + # Reduce control_channel_map based on which channels are in the model + if bool(control_channel_map_backend): + control_channel_map = {} + for label, idx in control_channel_map_backend.items(): + if f"u{idx}" in hamiltonian_channels: + control_channel_map[label] = idx + options["control_channel_map"] = control_channel_map + + # build the solver + if rotating_frame == "auto": + if "dense" in evaluation_mode: + rotating_frame = static_hamiltonian + else: + rotating_frame = np.diag(static_hamiltonian) + + # get time step size + if backend_target is not None and backend_target.dt is not None: + dt = backend_target.dt + else: + # config is guaranteed to have a dt + dt = backend_config.dt + + solver = Solver( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=hamiltonian_operators, + hamiltonian_channels=hamiltonian_channels, + channel_carrier_freqs=channel_freqs, + dt=dt, + rotating_frame=rotating_frame, + evaluation_mode=evaluation_mode, + rwa_cutoff_freq=rwa_cutoff_freq, + ) + + return cls( + solver=solver, + target=Target(dt=dt), + subsystem_dims=subsystem_dims, + **options, + )
+
+ + + +
+[docs] +def default_experiment_result_function( + experiment_name: str, + solver_result: OdeResult, + measurement_subsystems: List[int], + memory_slot_indices: List[int], + num_memory_slots: Union[None, int], + backend: DynamicsBackend, + seed: Optional[int] = None, + metadata: Optional[Dict] = None, +) -> ExperimentResult: + """Default routine for generating ExperimentResult object. + + To generate the results for a given experiment, this method takes the following steps: + + * The final state is transformed out of the rotating frame and into the lab frame using + ``backend.options.solver``. + * If ``backend.options.normalize_states==True``, the final state is normalized. + * Measurement results are computed, in the dressed basis, based on both the measurement-related + options in ``backend.options`` and the measurement specification extracted from the specific + experiment. + + Args: + experiment_name: Name of experiment. + solver_result: Result object from :class:`Solver.solve`. + measurement_subsystems: Labels of subsystems in the model being measured. + memory_slot_indices: Indices of memory slots to store the results in for each subsystem. + num_memory_slots: Total number of memory slots in the returned output. If ``None``, + ``max(memory_slot_indices)`` will be used. + backend: The backend instance that ran the simulation. Various options and properties + are utilized. + seed: Seed for any random number generation involved (e.g. when computing outcome samples). + metadata: Metadata to add to the header of the + :class:`~qiskit.result.models.ExperimentResult` object. + + Returns: + :class:`~qiskit.result.models.ExperimentResult` object containing results. + + Raises: + QiskitError: If a specified option is unsupported. + """ + + yf = solver_result.y[-1] + tf = solver_result.t[-1] + + # Take state out of frame, put in dressed basis, and normalize + if isinstance(yf, Statevector): + yf = np.array(backend.options.solver.model.rotating_frame.state_out_of_frame(t=tf, y=yf)) + yf = backend._dressed_states_adjoint @ yf + yf = Statevector(yf, dims=backend.options.subsystem_dims) + + if backend.options.normalize_states: + yf = yf / np.linalg.norm(yf.data) + elif isinstance(yf, DensityMatrix): + yf = np.array( + backend.options.solver.model.rotating_frame.operator_out_of_frame(t=tf, operator=yf) + ) + yf = backend._dressed_states_adjoint @ yf @ backend._dressed_states + yf = DensityMatrix(yf, dims=backend.options.subsystem_dims) + + if backend.options.normalize_states: + yf = yf / np.diag(yf.data).sum() + + if backend.options.meas_level == MeasLevel.CLASSIFIED: + memory_slot_probabilities = _get_memory_slot_probabilities( + probability_dict=yf.probabilities_dict(qargs=measurement_subsystems), + memory_slot_indices=memory_slot_indices, + num_memory_slots=num_memory_slots, + max_outcome_value=backend.options.max_outcome_level, + ) + + # sample + memory_samples = _sample_probability_dict( + memory_slot_probabilities, + shots=backend.options.shots, + normalize_probabilities=backend.options.normalize_states, + seed=seed, + ) + counts = _get_counts_from_samples(memory_samples) + + # construct results object + exp_data = ExperimentResultData( + counts=counts, memory=memory_samples if backend.options.memory else None + ) + return ExperimentResult( + shots=backend.options.shots, + success=True, + data=exp_data, + meas_level=MeasLevel.CLASSIFIED, + seed=seed, + header=QobjHeader(name=experiment_name, metadata=metadata), + ) + elif backend.options.meas_level == MeasLevel.KERNELED: + iq_centers = backend.options.iq_centers + if iq_centers is None: + # Default iq_centers + iq_centers = [] + for sub_dim in backend.options.subsystem_dims: + theta = 2 * np.pi / sub_dim + iq_centers.append( + [(np.cos(idx * theta), np.sin(idx * theta)) for idx in range(sub_dim)] + ) + + # generate IQ + measurement_data = _get_iq_data( + yf, + measurement_subsystems=measurement_subsystems, + iq_centers=iq_centers, + iq_width=backend.options.iq_width, + shots=backend.options.shots, + memory_slot_indices=memory_slot_indices, + num_memory_slots=num_memory_slots, + seed=seed, + ) + + if backend.options.meas_return == MeasReturnType.AVERAGE: + measurement_data = np.average(measurement_data, axis=0) + + # construct results object + exp_data = ExperimentResultData(memory=measurement_data) + return ExperimentResult( + shots=backend.options.shots, + success=True, + data=exp_data, + meas_level=MeasLevel.KERNELED, + seed=seed, + header=QobjHeader(name=experiment_name, metadata=metadata), + ) + + else: + raise QiskitError(f"meas_level=={backend.options.meas_level} not implemented.")
+ + + +def _validate_run_input(run_input, accept_list=True): + """Raise errors if the run_input is not one of QuantumCircuit, Schedule, ScheduleBlock, or + a list of these. + """ + if isinstance(run_input, list) and accept_list: + # if list apply recursively, but no longer accept lists + for x in run_input: + _validate_run_input(x, accept_list=False) + elif not isinstance(run_input, (QuantumCircuit, Schedule, ScheduleBlock)): + raise QiskitError(f"Input type {type(run_input)} not supported by DynamicsBackend.run.") + + +def _get_acquire_instruction_timings( + schedules: List[Schedule], subsystem_dims: List[int], dt: float +) -> Tuple[List[List[float]], List[List[int]], List[List[int]]]: + """Get the required data from the acquire commands in each schedule. + + Additionally validates that each schedule has Acquire instructions occurring at one time, at + least one memory slot is being listed, and all measured subsystem indices are less than + ``len(subsystem_dims)``. Additionally, a warning is raised if a 'trivial' subsystem is measured, + i.e. one with dimension 1. + + Args: + schedules: A list of schedules. + subsystem_dims: List of subsystem dimensions. + dt: The sample size. + Returns: + A tuple of lists containing, for each schedule: the list of integration intervals required + for each schedule (in absolute time, from 0.0 to the beginning of the acquire instructions), + a list of the subsystems being measured, and a list of the memory slots indices in which to + store the results of each subsystem measurement. + Raises: + QiskitError: If a schedule contains no measurement, if a schedule contains measurements at + different times, or if a measurement has an invalid subsystem label. + """ + t_span_list = [] + measurement_subsystems_list = [] + memory_slot_indices_list = [] + for schedule in schedules: + schedule_acquires = [] + schedule_acquire_times = [] + for start_time, inst in schedule.instructions: + # only track acquires saving in a memory slot + if isinstance(inst, pulse.Acquire) and inst.mem_slot is not None: + schedule_acquires.append(inst) + schedule_acquire_times.append(start_time) + + # validate + if len(schedule_acquire_times) == 0: + raise QiskitError( + "At least one measurement saving a a result in a MemorySlot " + "must be present in each schedule." + ) + + for acquire_time in schedule_acquire_times[1:]: + if acquire_time != schedule_acquire_times[0]: + raise QiskitError("DynamicsBackend.run only supports measurements at one time.") + + # use dt to convert acquire start time from sample index to the integration interval + t_span_list.append([0.0, dt * schedule_acquire_times[0]]) + measurement_subsystems = [] + memory_slot_indices = [] + for inst in schedule_acquires: + if not inst.channel.index < len(subsystem_dims): + raise QiskitError( + f"Attempted to measure out of bounds subsystem {inst.channel.index}." + ) + + if subsystem_dims[inst.channel.index] == 1: + warnings.warn(f"Measuring trivial subsystem {inst.channel.index} with dimension 1.") + + measurement_subsystems.append(inst.channel.index) + + memory_slot_indices.append(inst.mem_slot.index) + + measurement_subsystems_list.append(measurement_subsystems) + memory_slot_indices_list.append(memory_slot_indices) + + return t_span_list, measurement_subsystems_list, memory_slot_indices_list + + +def _to_schedule_list( + run_input: List[Union[QuantumCircuit, Schedule, ScheduleBlock]], backend: BackendV2 +): + """Convert all inputs to schedules, and store the number of classical registers present + in any circuits. + """ + if not isinstance(run_input, list): + run_input = [run_input] + + schedules = [] + num_memslots = [] + for sched in run_input: + num_memslots.append(None) + if isinstance(sched, ScheduleBlock): + schedules.append(block_to_schedule(sched)) + elif isinstance(sched, Schedule): + schedules.append(sched) + elif isinstance(sched, QuantumCircuit): + num_memslots[-1] = sum(creg.size for creg in sched.cregs) + schedules.append(build_schedule(sched, backend, dt=backend.options.solver._dt)) + else: + raise QiskitError(f"Type {type(sched)} cannot be converted to Schedule.") + return schedules, num_memslots + + +def _get_backend_channel_freqs( + backend_target: Optional[Target], + backend_config: PulseBackendConfiguration, + backend_defaults: Optional[PulseDefaults], + channels: List[str], +) -> Dict[str, float]: + """Extract frequencies of channels from a backend configuration and defaults. + + Args: + backend_target: A backend target object or ``None``. + backend_config: A backend configuration object. + backend_defaults: A backend defaults object or ``None``. + channels: Channel labels given as strings, assumed to be unique. + + Returns: + Dict: Mapping of channel labels to frequencies. + + Raises: + QiskitError: If the frequency for one of the channels cannot be found. + """ + + # partition types of channels + drive_channels = [] + meas_channels = [] + u_channels = [] + + for channel in channels: + if channel[0] == "d": + drive_channels.append(channel) + elif channel[0] == "m": + meas_channels.append(channel) + elif channel[0] == "u": + u_channels.append(channel) + else: + raise QiskitError("Unrecognized channel type requested.") + + # extract and validate channel frequency parameters + if drive_channels: + # get drive channel frequencies + drive_frequencies = [] + if (backend_target is not None) and (backend_target.qubit_properties is not None): + drive_frequencies = [q.frequency for q in backend_target.qubit_properties] + elif backend_defaults is not None: + drive_frequencies = backend_defaults.qubit_freq_est + else: + raise QiskitError( + "DriveChannels in model but frequencies not available in target or defaults." + ) + + if meas_channels: + if backend_defaults is not None: + meas_frequencies = backend_defaults.meas_freq_est + else: + raise QiskitError("MeasureChannels in model but defaults does not have meas_freq_est.") + + # backend_config.u_channel_lo is guaranteed to be a list + u_channel_lo = backend_config.u_channel_lo + + # populate frequencies + channel_freqs = {} + + for channel in drive_channels: + idx = int(channel[1:]) + if idx >= len(drive_frequencies): + raise QiskitError(f"DriveChannel index {idx} is out of bounds.") + channel_freqs[channel] = drive_frequencies[idx] + + for channel in meas_channels: + idx = int(channel[1:]) + if idx >= len(meas_frequencies): + raise QiskitError(f"MeasureChannel index {idx} is out of bounds.") + channel_freqs[channel] = meas_frequencies[idx] + + for channel in u_channels: + idx = int(channel[1:]) + if idx >= len(u_channel_lo): + raise QiskitError(f"ControlChannel index {idx} is out of bounds.") + freq = 0.0 + for channel_lo in u_channel_lo[idx]: + freq += drive_frequencies[channel_lo.q] * channel_lo.scale + + channel_freqs[channel] = freq + + # validate that all channels have frequencies + for channel in channels: + if channel not in channel_freqs: + raise QiskitError(f"No carrier frequency found for channel {channel}.") + + return channel_freqs +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/dispatch/dispatch.html b/stable/0.4/_modules/qiskit_dynamics/dispatch/dispatch.html new file mode 100644 index 000000000..ed943d436 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/dispatch/dispatch.html @@ -0,0 +1,695 @@ + + + + + + + + qiskit_dynamics.dispatch.dispatch - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.dispatch.dispatch

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+"""Dispatch class"""
+
+import functools
+from types import FunctionType
+from typing import Optional, Union, Tuple, Callable
+from .exceptions import DispatchError
+
+
+class Dispatch:
+    """Dispatch NumPy ufuncs to multiple array backends."""
+
+    # Registered backend names
+    REGISTERED_BACKENDS = tuple()
+
+    # Set version of registered backend name strings
+    _REGISTERED_BACKENDS = set()
+
+    # Registered backend array types
+    REGISTERED_TYPES = tuple()
+
+    # Set version of registered backend array types
+    _REGISTERED_TYPES = {}
+
+    # Default backend. If None the backend of the input array is used.
+    DEFAULT_BACKEND = None
+
+    # Dispatch table from backend name to asarray function
+    ASARRAY_DISPATCH = {}
+
+    # Dispatch table from backend name to repr function
+    REPR_DISPATCH = {}
+
+    # Dispatch table from backend name to array_ufunc dispatcher
+    ARRAY_UFUNC_DISPATCH = {}
+
+    # Dispatch table from backend name to array_function dispatcher
+    ARRAY_FUNCTION_DISPATCH = {}
+
+    @classmethod
+    def backend(
+        cls, array: any, subclass: Optional[bool] = False, fallback: Optional[str] = None
+    ) -> str:
+        """Return the registered backend string of a array object.
+
+        Args:
+            array: an array object.
+            subclass: If True check if array type is a subclass of
+                      registered types.
+            fallback: Fallback backend to use if array does not match any
+                      registered types.
+
+        Returns:
+            str: The array backend name if the array type is registered,
+                 or `use_default=True` and the type is not registered.
+            None: If `use_default=False` and the array type is not registered.
+
+        Raises:
+            DispatchError: if fallback backend is not valid.
+        """
+        backend = cls._REGISTERED_TYPES.get(type(array), None)
+        if backend is not None:
+            return backend
+        if subclass:
+            for key, backend in cls._REGISTERED_TYPES.items():
+                if isinstance(array, key):
+                    return backend
+        if fallback is None or fallback in cls._REGISTERED_BACKENDS:
+            return fallback
+        raise DispatchError(f"fallback '{fallback}' is not a registered backend.")
+
+    @classmethod
+    def validate_backend(cls, backend: str):
+        """Raise an exception if backend is not registered.
+
+        Args:
+            backend: array backend name.
+
+        Raises:
+            DispatchError: if backend is not registered.
+        """
+        if backend not in cls._REGISTERED_BACKENDS:
+            registered = cls.REGISTERED_BACKENDS if cls.REGISTERED_BACKENDS else None
+            raise DispatchError(
+                f"""'{backend}' is not a registered array backends
+                (registered backends: {registered})"""
+            )
+
+    @classmethod
+    def array_ufunc(cls, backend: str, ufunc: callable, method: str) -> callable:
+        """Return the ufunc for the specified array backend
+
+        Args:
+            backend: the array backend name.
+            ufunc: the numpy ufunc.
+            method: the ufunc method.
+
+        Returns:
+            callable: the ufunc for the specified array backend.
+        """
+        return cls.ARRAY_UFUNC_DISPATCH[backend](ufunc, method)
+
+    @classmethod
+    def repr(cls, backend: str) -> callable:
+        """Return the ufunc for the specified array backend
+
+        Args:
+            backend: the array backend name.
+
+        Returns:
+            callable: the repr function for the specified array backend.
+        """
+        return cls.REPR_DISPATCH[backend]
+
+    @classmethod
+    def array_function(cls, backend: str, func: callable) -> callable:
+        """Return the array function for the specified array backend
+
+        Args:
+            backend: the array backend name.
+            func: the numpy array function.
+
+        Returns:
+            callable: the function for the specified array backend.
+        """
+        return cls.ARRAY_FUNCTION_DISPATCH[backend](func)
+
+    @classmethod
+    def register_types(cls, name: str, array_types: Union[any, Tuple[any]]):
+        """Register an asarray array backend.
+
+        Args:
+            name: A string to identify the array backend.
+            array_types: A class or list of classes to associate
+                         with the array backend.
+        """
+        cls._REGISTERED_BACKENDS.add(name)
+        cls.REGISTERED_BACKENDS = tuple(cls._REGISTERED_BACKENDS)
+        if not isinstance(array_types, (list, tuple)):
+            array_types = [array_types]
+        for atype in array_types:
+            cls._REGISTERED_TYPES[atype] = name
+        cls.REGISTERED_TYPES = tuple(cls._REGISTERED_TYPES.keys())
+
+    @classmethod
+    def register_asarray(
+        cls, name: str, array_types: Optional[Union[any, Tuple[any]]] = None
+    ) -> callable:
+        """Decorator to register an asarray function for an array backend.
+
+        The function being wrapped must have signature
+        `func(arg, dtype=None, order=None)`.
+
+        Args:
+            name: A string to identify the array backend.
+            array_types: Optional, the array types to register
+                         for the backend.
+
+        Returns:
+            callable: the decorated function.
+        """
+        if array_types:
+            cls.register_types(name, array_types)
+
+        def decorator(func):
+            cls.ASARRAY_DISPATCH[name] = func
+            return func
+
+        return decorator
+
+    @classmethod
+    def register_repr(
+        cls, name: str, array_types: Optional[Union[any, Tuple[any]]] = None
+    ) -> callable:
+        """Decorator to register an asarray function for an array backend.
+
+        The function being wrapped must have signature
+        `func(arg, dtype=None, order=None)`.
+
+        Args:
+            name: A string to identify the array backend.
+            array_types: Optional, the array types to register
+                         for the backend.
+
+        Returns:
+            callable: the decorated function.
+        """
+        if array_types:
+            cls.register_types(name, array_types)
+
+        def decorator(func):
+            cls.REPR_DISPATCH[name] = func
+            return func
+
+        return decorator
+
+    @classmethod
+    def register_array_ufunc(
+        cls, name: str, array_types: Optional[Union[any, Tuple[any]]] = None
+    ) -> callable:
+        """Decorator to register a ufunc dispatch function.
+
+        This is used for handling of dispatch of NumPy ufuncs using
+        `__array_ufunc__`. The function being wrapped must have
+        signature `f(ufunc, method) -> callable(*args, **kwargs)`
+
+        Args:
+            name: A string to identify the array backend.
+            array_types: Optional, the array types to register
+                         for the backend.
+
+        Returns:
+            callable: the decorated function.
+        """
+        if array_types:
+            cls.register_types(name, array_types)
+
+        def decorator(func):
+            cls.ARRAY_UFUNC_DISPATCH[name] = func
+            return func
+
+        return decorator
+
+    @classmethod
+    def register_array_function(
+        cls, name: str, array_types: Optional[Union[any, Tuple[any]]] = None
+    ) -> callable:
+        """Decorator to register an array function dispatch function.
+
+        This is used for handling of dispatch of NumPy functions using
+        `__array_function__`. The function being wrapped must have
+        signature `f(func) -> callable(*args, **kwargs)`.
+
+        Args:
+            name: A string to identify the array backend.
+            array_types: Optional, the array types to register
+                         for the backend.
+
+        Returns:
+            callable: the decorated function.
+        """
+        if array_types:
+            cls.register_types(name, array_types)
+
+        def decorator(func):
+            cls.ARRAY_FUNCTION_DISPATCH[name] = func
+            return func
+
+        return decorator
+
+    @staticmethod
+    def implements(np_function, dispatch_dict):
+        """Register a numpy __array_function__ implementation."""
+
+        def decorator(func):
+            dispatch_dict[np_function] = func
+            return func
+
+        return decorator
+
+
+
+[docs] +def asarray( + array: any, + dtype: Optional[any] = None, + order: Optional[str] = None, + backend: Optional[str] = None, +) -> any: + """Convert input array to an array on the specified backend. + + This functions like `numpy.asarray` but optionally supports + casting to other registered array backends. + + Args: + array: An array_like input. + dtype: Optional. The dtype of the returned array. This value + must be supported by the specified array backend. + order: Optional. The array order. This value must be supported + by the specified array backend. + backend: A registered array backend name. If None the + default array backend will be used. + + Returns: + array: an array object of the form specified by the backend + kwarg. + """ + if backend: + Dispatch.validate_backend(backend) + else: + if Dispatch.DEFAULT_BACKEND: + backend = Dispatch.DEFAULT_BACKEND + else: + backend = Dispatch.backend(array, fallback="numpy") + return Dispatch.ASARRAY_DISPATCH[backend](array, dtype=dtype, order=order)
+ + + +
+[docs] +def requires_backend(backend: str) -> Callable: + """Return a function and class decorator for checking a backend is available. + + If the the required backend is not in the list of :func:`available_backends` + any decorated function or method will raise an exception when called, and + any decorated class will raise an exeption when its ``__init__`` is called. + + Args: + backend: the backend name required by class or function. + + Returns: + Callable: A decorator that may be used to specify that a function, class, + or class method requires a specific backend to be installed. + """ + + def decorator(obj): + """Specify that the decorated object requires a specifc Array backend.""" + + def check_backend(descriptor): + if backend not in Dispatch.REGISTERED_BACKENDS: + raise DispatchError( + f"Array backend '{backend}' required by {descriptor} " + "is not installed. Please install the optional " + "library '{backend}'." + ) + + # Decorate a function or method + if isinstance(obj, FunctionType): + + @functools.wraps(obj) + def decorated_func(*args, **kwargs): + check_backend(f"function {obj}") + return obj(*args, **kwargs) + + return decorated_func + + # Decorate a class + elif isinstance(obj, type): + obj_init = obj.__init__ + + @functools.wraps(obj_init) + def decorated_init(self, *args, **kwargs): + check_backend(f"class {obj}") + obj_init(self, *args, **kwargs) + + obj.__init__ = decorated_init + return obj + + else: + raise Exception(f"Cannot decorate object {obj} that is not a class or function.") + + return decorator
+ +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/models/generator_model.html b/stable/0.4/_modules/qiskit_dynamics/models/generator_model.html new file mode 100644 index 000000000..6af9b9403 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/models/generator_model.html @@ -0,0 +1,914 @@ + + + + + + + + qiskit_dynamics.models.generator_model - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.models.generator_model

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Generator models module.
+"""
+
+from abc import ABC, abstractmethod
+from typing import Tuple, Union, List, Optional
+from warnings import warn
+from copy import copy
+import numpy as np
+from scipy.sparse import csr_matrix, issparse, diags
+
+from qiskit import QiskitError
+from qiskit.quantum_info.operators import Operator
+from qiskit_dynamics.models.operator_collections import (
+    BaseOperatorCollection,
+    DenseOperatorCollection,
+    SparseOperatorCollection,
+    JAXSparseOperatorCollection,
+)
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.signals import Signal, SignalList
+from qiskit_dynamics.type_utils import to_numeric_matrix_type
+from .rotating_frame import RotatingFrame
+
+try:
+    import jax
+except ImportError:
+    pass
+
+
+class BaseGeneratorModel(ABC):
+    r"""Defines an interface for a time-dependent linear differential equation of the form
+    :math:`\dot{y}(t) = \Lambda(t, y)`, where :math:`\Lambda` is linear in :math:`y`. The core
+    functionality is evaluation of :math:`\Lambda(t, y)`, as well as, if possible, evaluation of the
+    map :math:`\Lambda(t, \cdot)`.
+
+    Additionally, the class defines interfaces for:
+
+     * Setting a "rotating frame", specified either directly as a :class:`RotatingFrame`
+        instance, or an operator from which a :class:`RotatingFrame` instance can be constructed.
+        The exact meaning of this transformation is determined by the structure of
+        :math:`\Lambda(t, y)`, and is therefore by handled by concrete subclasses.
+     * Setting an internal "evaluation mode", to set the specific numerical methods to use
+        when evaluating :math:`\Lambda(t, y)`.
+    """
+
+    @property
+    @abstractmethod
+    def dim(self) -> int:
+        """The matrix dimension."""
+
+    @property
+    @abstractmethod
+    def rotating_frame(self) -> RotatingFrame:
+        """The rotating frame."""
+
+    @rotating_frame.setter
+    @abstractmethod
+    def rotating_frame(self, rotating_frame: RotatingFrame):
+        pass
+
+    @property
+    @abstractmethod
+    def in_frame_basis(self) -> bool:
+        """Whether or not the model is evaluated in the basis in which the frame is diagonalized."""
+
+    @in_frame_basis.setter
+    @abstractmethod
+    def in_frame_basis(self, in_frame_basis: bool):
+        pass
+
+    @property
+    @abstractmethod
+    def evaluation_mode(self) -> str:
+        """Numerical evaluation mode of the model."""
+
+    @evaluation_mode.setter
+    @abstractmethod
+    def evaluation_mode(self, new_mode: str):
+        pass
+
+    @abstractmethod
+    def evaluate(self, time: float) -> Array:
+        r"""If possible, evaluate the map :math:`\Lambda(t, \cdot)`.
+
+        Args:
+            time: The time to evaluate the model at.
+        """
+
+    @abstractmethod
+    def evaluate_rhs(self, time: float, y: Array) -> Array:
+        r"""Evaluate the right hand side :math:`\dot{y}(t) = \Lambda(t, y)`.
+
+        Args:
+            time: The time to evaluate the model at.
+            y: State of the differential equation.
+        """
+
+    def copy(self):
+        """Return a copy of self."""
+        return copy(self)
+
+    def __call__(self, time: float, y: Optional[Array] = None) -> Array:
+        r"""Evaluate generator RHS functions. If ``y is None``, attemps to evaluate
+        :math:`\Lambda(t, \cdot)`, otherwise, calculates :math:`\Lambda(t, y)`.
+
+        Args:
+            time: The time to evaluate at.
+            y: Optional state.
+
+        Returns:
+            Array: Either the evaluated model, or the RHS for the given y.
+        """
+        return self.evaluate(time) if y is None else self.evaluate_rhs(time, y)
+
+
+
+[docs] +class GeneratorModel(BaseGeneratorModel): + r"""A model for a a linear matrix differential equation in standard form. + + :class:`GeneratorModel` is a concrete instance of :class:`BaseGeneratorModel`, where the map + :math:`\Lambda(t, y)` is explicitly constructed as: + + .. math:: + + \Lambda(t, y) = G(t)y, + + G(t) = \sum_i s_i(t) G_i + G_d + + where the :math:`G_i` are matrices (represented by :class:`Operator` or :class:`Array` objects), + the :math:`s_i(t)` are signals represented by a list of :class:`Signal` objects, and :math:`G_d` + is the constant-in-time static term of the generator. + """ + + def __init__( + self, + static_operator: Optional[Array] = None, + operators: Optional[Array] = None, + signals: Optional[Union[SignalList, List[Signal]]] = None, + rotating_frame: Optional[Union[Operator, Array, RotatingFrame]] = None, + in_frame_basis: bool = False, + evaluation_mode: str = "dense", + ): + """Initialize. + + Args: + static_operator: Constant part of the generator. + operators: A list of operators :math:`G_i`. + signals: Stores the terms :math:`s_i(t)`. While required for evaluation, + :class:`GeneratorModel` signals are not required at instantiation. + rotating_frame: Rotating frame operator. + in_frame_basis: Whether to represent the model in the basis in which the rotating frame + operator is diagonalized. + evaluation_mode: Evaluation mode to use. Supported options are ``'dense'`` and + ``'sparse'``. Call ``help(GeneratorModel.evaluation_mode)`` for more details. + Raises: + QiskitError: If model not sufficiently specified. + """ + if static_operator is None and operators is None: + raise QiskitError( + f"{type(self).__name__} requires at least one of static_operator or operators to " + "be specified at construction." + ) + + # initialize internal operator representation + self._operator_collection = construct_operator_collection( + evaluation_mode=evaluation_mode, static_operator=static_operator, operators=operators + ) + self._evaluation_mode = evaluation_mode + + # set frame + self._rotating_frame = None + self.rotating_frame = rotating_frame + + # set operation in frame basis + self._in_frame_basis = None + self.in_frame_basis = in_frame_basis + + # initialize signal-related attributes + self.signals = signals + + @property + def dim(self) -> int: + """The matrix dimension.""" + return self._operator_collection.dim + + @property + def evaluation_mode(self) -> str: + """Numerical evaluation mode of the model. + + Possible values: + + * ``"dense"``: Stores/evaluates operators using dense Arrays. + * ``"sparse"``: Stores/evaluates operators using sparse matrices. If the default Array + backend is JAX, implemented with JAX BCOO arrays, otherwise uses scipy + :class:`csr_matrix` sparse type. Note that JAX sparse mode is only recommended for use on + CPU. + + Raises: + NotImplementedError: If, when being set, ``new_mode`` is not one of the above supported + evaluation modes. + """ + return self._evaluation_mode + + @evaluation_mode.setter + def evaluation_mode(self, new_mode: str): + if new_mode != self._evaluation_mode: + self._operator_collection = construct_operator_collection( + new_mode, + self._operator_collection.static_operator, + self._operator_collection.operators, + ) + self._evaluation_mode = new_mode + + @property + def rotating_frame(self) -> RotatingFrame: + """The rotating frame. + + This property can be set with a :class:`RotatingFrame` instance, or any valid constructor + argument to this class. + + Setting this property updates the internal operator to use the new frame. + """ + return self._rotating_frame + + @rotating_frame.setter + def rotating_frame(self, rotating_frame: Union[Operator, Array, RotatingFrame]): + new_frame = RotatingFrame(rotating_frame) + + new_static_operator = transfer_static_operator_between_frames( + self._get_static_operator(in_frame_basis=True), + new_frame=new_frame, + old_frame=self.rotating_frame, + ) + new_operators = transfer_operators_between_frames( + self._get_operators(in_frame_basis=True), + new_frame=new_frame, + old_frame=self.rotating_frame, + ) + + self._rotating_frame = new_frame + + self._operator_collection = construct_operator_collection( + self.evaluation_mode, new_static_operator, new_operators + ) + + @property + def signals(self) -> SignalList: + """The signals in the model. + + Raises: + QiskitError: If set to ``None`` when operators exist, or when set to a number of signals + different then the number of operators. + """ + return self._signals + + @signals.setter + def signals(self, signals: Union[SignalList, List[Signal]]): + if signals is None: + self._signals = None + elif signals is not None and self.operators is None: + raise QiskitError("Signals must be None if operators is None.") + else: + # if signals is a list, instantiate a SignalList + if isinstance(signals, list): + signals = SignalList(signals) + + # if it isn't a SignalList by now, raise an error + if not isinstance(signals, SignalList): + raise QiskitError("Signals specified in unaccepted format.") + + # verify signal length is same as operators + if isinstance(self.operators, list): + len_operators = len(self.operators) + else: + len_operators = self.operators.shape[0] + if len(signals) != len_operators: + raise QiskitError("Signals needs to have the same length as operators.") + + self._signals = signals + + @property + def in_frame_basis(self) -> bool: + """Whether the model is represented in the basis that diagonalizes the frame operator.""" + return self._in_frame_basis + + @in_frame_basis.setter + def in_frame_basis(self, in_frame_basis: bool): + self._in_frame_basis = in_frame_basis + + @property + def operators(self) -> Array: + """The operators in the model.""" + return self._get_operators(in_frame_basis=self._in_frame_basis) + + @property + def static_operator(self) -> Array: + """The static operator.""" + return self._get_static_operator(in_frame_basis=self._in_frame_basis) + + @static_operator.setter + def static_operator(self, static_operator: Array): + self._set_static_operator( + new_static_operator=static_operator, operator_in_frame_basis=self._in_frame_basis + ) + + def _get_operators(self, in_frame_basis: Optional[bool] = False) -> Array: + """Get the operators used in the model construction. + + Args: + in_frame_basis: Flag indicating whether to return the operators + in the basis in which the rotating frame operator is diagonal. + Returns: + The operators in the basis specified by ``in_frame_basis``. + """ + ops = self._operator_collection.operators + if ops is not None and not in_frame_basis and self.rotating_frame is not None: + return self.rotating_frame.operator_out_of_frame_basis(ops) + return ops + + def _get_static_operator(self, in_frame_basis: Optional[bool] = False) -> Array: + """Get the constant term. + + Args: + in_frame_basis: Whether the returned static_operator should be in the basis in which the + frame is diagonal. + Returns: + The static operator term. + """ + op = self._operator_collection.static_operator + if not in_frame_basis and self.rotating_frame is not None: + return self.rotating_frame.operator_out_of_frame_basis(op) + return op + + def _set_static_operator( + self, + new_static_operator: Array, + operator_in_frame_basis: Optional[bool] = False, + ): + """Sets static term. Note that if the model has a rotating frame this will override any + contributions to the static term due to the frame transformation. + + Args: + new_static_operator: The static operator operator. + operator_in_frame_basis: Whether `new_static_operator` is already in the rotating frame + basis. + """ + if new_static_operator is None: + self._operator_collection.static_operator = None + else: + if not operator_in_frame_basis and self.rotating_frame is not None: + new_static_operator = self.rotating_frame.operator_into_frame_basis( + new_static_operator + ) + + self._operator_collection.static_operator = new_static_operator + +
+[docs] + def evaluate(self, time: float) -> Array: + """Evaluate the model in array format as a matrix, independent of state. + + Args: + time: The time to evaluate the model at. + + Returns: + Array: The evaluated model as a matrix. + + Raises: + QiskitError: If model cannot be evaluated. + """ + if self._signals is None and self._operator_collection.operators is not None: + raise QiskitError( + f"{type(self).__name__} with non-empty operators must be evaluated signals." + ) + + # Evaluated in frame basis, but without rotations + op_combo = self._operator_collection(self._signals(time) if self._signals else None) + + # Apply rotations e^{-Ft}Ae^{Ft} in frame basis where F = D + return self.rotating_frame.operator_into_frame( + time, op_combo, operator_in_frame_basis=True, return_in_frame_basis=self._in_frame_basis + )
+ + +
+[docs] + def evaluate_rhs(self, time: float, y: Array) -> Array: + r"""Evaluate ``G(t) @ y``. + + Args: + time: The time to evaluate the model at . + y: Array specifying system state. + + Returns: + Array defined by :math:`G(t) \times y`. + + Raises: + QiskitError: If model cannot be evaluated. + """ + if self._signals is None: + if self._operator_collection.operators is not None: + raise QiskitError( + f"{type(self).__name__} with non-empty operators must be evaluated signals." + ) + sig_vals = None + else: + sig_vals = self._signals.__call__(time) + + if self.rotating_frame is not None: + # First, compute e^{tF}y as a pre-rotation in the frame basis + out = self.rotating_frame.state_out_of_frame( + time, y, y_in_frame_basis=self._in_frame_basis, return_in_frame_basis=True + ) + # Then, compute the product Ae^{tF}y + out = self._operator_collection(sig_vals, out) + # Finally, we have the full operator e^{-tF}Ae^{tF}y + out = self.rotating_frame.state_into_frame( + time, out, y_in_frame_basis=True, return_in_frame_basis=self._in_frame_basis + ) + return out + + return self._operator_collection(sig_vals, y)
+
+ + + +def transfer_static_operator_between_frames( + static_operator: Union[None, Array, csr_matrix], + new_frame: Optional[Union[Array, RotatingFrame]] = None, + old_frame: Optional[Union[Array, RotatingFrame]] = None, +) -> Tuple[Union[None, Array]]: + """Helper function for transforming the static operator of a model from one frame basis into + another. + + ``static_operator`` is additionally transformed as a generator: the old frame operator is added, + and the new one is subtracted. + + Args: + static_operator: Static operator of the model. None is treated as 0. + new_frame: New rotating frame. + old_frame: Old rotating frame. + + Returns: + static_operator: Transformed as described above. + """ + new_frame = RotatingFrame(new_frame) + old_frame = RotatingFrame(old_frame) + + static_operator = to_numeric_matrix_type(static_operator) + + # transform out of old frame basis, and add the old frame operator + if static_operator is not None: + static_operator = old_frame.generator_out_of_frame( + t=0.0, + operator=static_operator, + operator_in_frame_basis=True, + return_in_frame_basis=False, + ) + else: + # "add" the frame operator to 0 + if old_frame.frame_operator is not None: + if issparse(static_operator): + if old_frame.frame_operator.ndim == 1: + static_operator = diags(old_frame.frame_operator, format="csr") + else: + static_operator = csr_matrix(old_frame.frame_operator) + else: + if old_frame.frame_operator.ndim == 1: + static_operator = np.diag(old_frame.frame_operator) + else: + static_operator = old_frame.frame_operator + # transform into new frame basis, and add the new frame operator + if static_operator is not None: + static_operator = new_frame.generator_into_frame( + t=0.0, + operator=static_operator, + operator_in_frame_basis=False, + return_in_frame_basis=True, + ) + else: + # "subtract" the frame operator from 0 + if new_frame.frame_operator is not None: + if issparse(static_operator): + static_operator = -diags(new_frame.frame_diag, format="csr") + else: + static_operator = -np.diag(new_frame.frame_diag) + + return static_operator + + +def transfer_operators_between_frames( + operators: Union[None, Array, List[csr_matrix]], + new_frame: Optional[Union[Array, RotatingFrame]] = None, + old_frame: Optional[Union[Array, RotatingFrame]] = None, +) -> Tuple[Union[None, Array]]: + """Helper function for transforming a list of operators for a model from one frame basis into + another. + + Args: + operators: Operators of the model. If ``None``, remains as ``None``. + new_frame: New rotating frame. + old_frame: Old rotating frame. + + Returns: + operators: Transformed as described above. + """ + new_frame = RotatingFrame(new_frame) + old_frame = RotatingFrame(old_frame) + + operators = to_numeric_matrix_type(operators) + + # transform out of old frame basis + if operators is not None: + # For sparse case, if list, loop + if isinstance(operators, list): + operators = [old_frame.operator_out_of_frame_basis(op) for op in operators] + else: + operators = old_frame.operator_out_of_frame_basis(operators) + + # transform into new frame basis + if operators is not None: + # For sparse case, if list, loop + if isinstance(operators, list): + operators = [new_frame.operator_into_frame_basis(op) for op in operators] + else: + operators = new_frame.operator_into_frame_basis(operators) + + return operators + + +def construct_operator_collection( + evaluation_mode: str, + static_operator: Union[None, Array, csr_matrix], + operators: Union[None, Array, List[csr_matrix]], +) -> BaseOperatorCollection: + """Construct an operator collection for :class:`GeneratorModel`. + + Args: + evaluation_mode: Evaluation mode. + static_operator: Static operator of the model. + operators: Operators for the model. + + Returns: + BaseOperatorCollection: The relevant operator collection. + + Raises: + NotImplementedError: If the ``evaluation_mode`` is invalid. + """ + + if evaluation_mode == "dense": + return DenseOperatorCollection(static_operator=static_operator, operators=operators) + if evaluation_mode == "sparse" and Array.default_backend() == "jax": + # warn that sparse mode when using JAX is primarily recommended for use on CPU + if jax.default_backend() != "cpu": + warn( + """Using sparse mode with JAX is primarily recommended for use on CPU.""", + stacklevel=2, + ) + + return JAXSparseOperatorCollection(static_operator=static_operator, operators=operators) + if evaluation_mode == "sparse": + return SparseOperatorCollection(static_operator=static_operator, operators=operators) + + raise NotImplementedError( + f"Evaluation mode '{evaluation_mode}' is not supported. Call " + "help(GeneratorModel.evaluation_mode) for available options." + ) +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/models/hamiltonian_model.html b/stable/0.4/_modules/qiskit_dynamics/models/hamiltonian_model.html new file mode 100644 index 000000000..5f3034a4f --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/models/hamiltonian_model.html @@ -0,0 +1,561 @@ + + + + + + + + qiskit_dynamics.models.hamiltonian_model - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.models.hamiltonian_model

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Hamiltonian models module.
+"""
+
+from typing import Union, List, Optional
+import numpy as np
+from scipy.sparse import csr_matrix, issparse
+from scipy.sparse.linalg import norm as spnorm
+
+from qiskit import QiskitError
+from qiskit.quantum_info.operators import Operator
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.signals import Signal, SignalList
+from qiskit_dynamics.type_utils import to_numeric_matrix_type, to_array
+from .generator_model import (
+    GeneratorModel,
+    transfer_static_operator_between_frames,
+    transfer_operators_between_frames,
+    construct_operator_collection,
+)
+from .rotating_frame import RotatingFrame
+
+
+
+[docs] +class HamiltonianModel(GeneratorModel): + r"""A model of a Hamiltonian for the Schrodinger equation. + + This class represents a Hamiltonian as a time-dependent decomposition the form: + + .. math:: + H(t) = H_d + \sum_j s_j(t) H_j, + + where :math:`H_j` are Hermitian operators, :math:`H_d` is the static component, and the + :math:`s_j(t)` are either :class:`~qiskit_dynamics.signals.Signal` objects or numerical + constants. Constructing a :class:`~qiskit_dynamics.models.HamiltonianModel` requires specifying + the above decomposition, e.g.: + + .. code-block:: python + + hamiltonian = HamiltonianModel(static_operator=static_operator, + operators=operators, + signals=signals) + + This class inherits most functionality from :class:`GeneratorModel`, with the following + modifications: + + * The operators :math:`H_d` and :math:`H_j` are assumed and verified to be Hermitian. + * Rotating frames are dealt with assuming the structure of the Schrodinger equation. I.e. + Evaluating the Hamiltonian :math:`H(t)` in a frame :math:`F = -iH_0`, evaluates the + expression :math:`e^{-tF}H(t)e^{tF} - H_0`. + """ + + def __init__( + self, + static_operator: Optional[Array] = None, + operators: Optional[List[Operator]] = None, + signals: Optional[Union[SignalList, List[Signal]]] = None, + rotating_frame: Optional[Union[Operator, Array, RotatingFrame]] = None, + in_frame_basis: bool = False, + evaluation_mode: str = "dense", + validate: bool = True, + ): + """Initialize, ensuring that the operators are Hermitian. + + Args: + static_operator: Time-independent term in the Hamiltonian. + operators: List of Operator objects. + signals: List of coefficients :math:`s_i(t)`. Not required at instantiation, but + necessary for evaluation of the model. + rotating_frame: Rotating frame operator. + If specified with a 1d array, it is interpreted as the + diagonal of a diagonal matrix. Assumed to store + the antihermitian matrix F = -iH. + in_frame_basis: Whether to represent the model in the basis in which the rotating + frame operator is diagonalized. + evaluation_mode: Evaluation mode to use. Supported options are ``'dense'`` and + ``'sparse'``. Call ``help(HamiltonianModel.evaluation_mode)`` for more + details. + validate: If True check input operators are Hermitian. + + Raises: + QiskitError: if operators are not Hermitian + """ + + # verify operators are Hermitian, and if so instantiate + operators = to_numeric_matrix_type(operators) + static_operator = to_numeric_matrix_type(static_operator) + + if validate: + if (operators is not None) and (not is_hermitian(operators)): + raise QiskitError("""HamiltonianModel operators must be Hermitian.""") + if (static_operator is not None) and (not is_hermitian(static_operator)): + raise QiskitError("""HamiltonianModel static_operator must be Hermitian.""") + + super().__init__( + static_operator=static_operator, + operators=operators, + signals=signals, + rotating_frame=rotating_frame, + in_frame_basis=in_frame_basis, + evaluation_mode=evaluation_mode, + ) + + @property + def rotating_frame(self) -> RotatingFrame: + """The rotating frame. + + This property can be set with a :class:`RotatingFrame` instance, or any valid constructor + argument to this class. It can either be Hermitian or anti-Hermitian, and in either case + only the anti-Hermitian version :math:`F=-iH` will be stored. + + Setting this property updates the internal operator list to use the new frame. + """ + return super().rotating_frame + + @rotating_frame.setter + def rotating_frame(self, rotating_frame: Union[Operator, Array, RotatingFrame]) -> Array: + new_frame = RotatingFrame(rotating_frame) + + # convert static operator to new frame setup + static_op = self._get_static_operator(in_frame_basis=True) + if static_op is not None: + static_op = -1j * static_op + + new_static_operator = transfer_static_operator_between_frames( + static_op, + new_frame=new_frame, + old_frame=self.rotating_frame, + ) + + if new_static_operator is not None: + new_static_operator = 1j * new_static_operator + + # convert operators to new frame set up + new_operators = transfer_operators_between_frames( + self._get_operators(in_frame_basis=True), + new_frame=new_frame, + old_frame=self.rotating_frame, + ) + + self._rotating_frame = new_frame + + self._operator_collection = construct_operator_collection( + self.evaluation_mode, new_static_operator, new_operators + ) + +
+[docs] + def evaluate(self, time: float) -> Array: + """Evaluate the model in array format as a matrix, independent of state. + + Args: + time: The time to evaluate the model at. + + Returns: + Array: The evaluated model as an anti-Hermitian matrix. + + Raises: + QiskitError: If model cannot be evaluated. + """ + return -1j * super().evaluate(time)
+ + +
+[docs] + def evaluate_rhs(self, time: float, y: Array) -> Array: + r"""Evaluate ``-1j * H(t) @ y``. + + Args: + time: The time to evaluate the model at . + y: Array specifying system state. + + Returns: + Array defined by :math:`G(t) \times y`. + + Raises: + QiskitError: If model cannot be evaluated. + """ + return -1j * super().evaluate_rhs(time, y)
+
+ + + +def is_hermitian( + operators: Union[Array, csr_matrix, List[csr_matrix], "BCOO"], tol: Optional[float] = 1e-10 +) -> bool: + """Validate that operators are Hermitian. + + Args: + operators: Either a 2d array representing a single operator, a 3d array representing a list + of operators, a ``csr_matrix``, or a list of ``csr_matrix``. + tol: Tolerance for checking zeros. + + Returns: + bool: Whether or not the operators are Hermitian to within tolerance. + + Raises: + QiskitError: If an unexpeted type is received. + """ + if isinstance(operators, (np.ndarray, Array)): + adj = None + if operators.ndim == 2: + adj = np.transpose(np.conjugate(operators)) + elif operators.ndim == 3: + adj = np.transpose(np.conjugate(operators), (0, 2, 1)) + return np.linalg.norm(adj - operators) < tol + elif issparse(operators): + return spnorm(operators - operators.conj().transpose()) < tol + elif isinstance(operators, list) and issparse(operators[0]): + return all(spnorm(op - op.conj().transpose()) < tol for op in operators) + elif type(operators).__name__ == "BCOO": + # fall back on array case for BCOO + return is_hermitian(to_array(operators)) + + raise QiskitError("is_hermitian got an unexpected type.") +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/models/lindblad_model.html b/stable/0.4/_modules/qiskit_dynamics/models/lindblad_model.html new file mode 100644 index 000000000..d0b3234e3 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/models/lindblad_model.html @@ -0,0 +1,1059 @@ + + + + + + + + qiskit_dynamics.models.lindblad_model - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.models.lindblad_model

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Lindblad model module.
+"""
+
+from typing import Tuple, Union, List, Optional
+from warnings import warn
+from scipy.sparse import csr_matrix
+
+from qiskit import QiskitError
+from qiskit.quantum_info.operators import Operator
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.type_utils import to_numeric_matrix_type
+from qiskit_dynamics.signals import Signal, SignalList
+from .generator_model import (
+    BaseGeneratorModel,
+    transfer_static_operator_between_frames,
+    transfer_operators_between_frames,
+)
+from .hamiltonian_model import HamiltonianModel, is_hermitian
+from .operator_collections import (
+    BaseLindbladOperatorCollection,
+    DenseLindbladCollection,
+    DenseVectorizedLindbladCollection,
+    SparseLindbladCollection,
+    JAXSparseLindbladCollection,
+    SparseVectorizedLindbladCollection,
+    JAXSparseVectorizedLindbladCollection,
+)
+from .rotating_frame import RotatingFrame
+
+try:
+    import jax
+except ImportError:
+    pass
+
+
+
+[docs] +class LindbladModel(BaseGeneratorModel): + r"""A model of a quantum system in terms of the Lindblad master equation. + + The Lindblad master equation models the evolution of a density matrix according to: + + .. math:: + + \dot{\rho}(t) = -i[H(t), \rho(t)] + \mathcal{D}_0(\rho(t)) + \mathcal{D}(t)(\rho(t)), + + where :math:`\mathcal{D}_0` is the static dissipator portion, and :math:`\mathcal{D}(t)` is the + time-dependent dissipator portion of the equation, given by + + .. math:: + + \mathcal{D}_0(\rho(t)) = \sum_j N_j \rho N_j^\dagger + - \frac{1}{2} \{N_j^\dagger N_j, \rho\}, + + and + + .. math:: + + \mathcal{D}(t)(\rho(t)) = \sum_j \gamma_j(t) L_j \rho L_j^\dagger + - \frac{1}{2} \{L_j^\dagger L_j, \rho\}, + + respectively. In the above: + + - :math:`[\cdot, \cdot]` and :math:`\{\cdot, \cdot\}` respectively denote the matrix + commutator and anti-commutator, + - :math:`H(t)` denotes the Hamiltonian, + - :math:`N_j` denotes the operators appearing in the static dissipator, + - :math:`L_j` denotes the operators appearing in the time-dpendent portion of the + dissipator, and + - :math:`\gamma_j(t)` denotes the signal corresponding to the + :math:`j^{th}` time-dependent dissipator operator. + + Instantiating an instance of :class:`~qiskit_dynamics.models.LindbladModel` requires specifying + the above decomposition: + + .. code-block:: python + + lindblad_model = LindbladModel( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=hamiltonian_operators, + hamiltonian_signals=hamiltonian_signals, + static_dissipators=static_dissipators, + dissipator_operators=dissipator_operators, + dissipator_signals=dissipator_signals + ) + + where the arguments ``hamiltonian_operators``, ``hamiltonian_signals``, and + ``static_hamiltonian`` are for the Hamiltonian decomposition as in + :class:`~qiskit_dynamics.models.HamiltonianModel`, + and the ``static_dissipators`` correspond to the :math:`N_j`, the ``dissipator_operators`` + to the :math:`L_j`, and the ``dissipator_signals`` the :math:`\gamma_j(t)`. + """ + + def __init__( + self, + static_hamiltonian: Optional[Union[Array, csr_matrix]] = None, + hamiltonian_operators: Optional[Union[Array, List[csr_matrix]]] = None, + hamiltonian_signals: Optional[Union[List[Signal], SignalList]] = None, + static_dissipators: Optional[Union[Array, csr_matrix]] = None, + dissipator_operators: Optional[Union[Array, List[csr_matrix]]] = None, + dissipator_signals: Optional[Union[List[Signal], SignalList]] = None, + rotating_frame: Optional[Union[Operator, Array, RotatingFrame]] = None, + in_frame_basis: bool = False, + evaluation_mode: Optional[str] = "dense", + validate: bool = True, + ): + """Initialize. + + Args: + static_hamiltonian: Constant term in Hamiltonian. + hamiltonian_operators: List of operators in Hamiltonian with time-dependent + coefficients. + hamiltonian_signals: Time-dependent coefficients for hamiltonian_operators. + static_dissipators: List of dissipators with coefficient 1. + dissipator_operators: List of dissipator operators with time-dependent coefficients. + dissipator_signals: Time-dependent coefficients for dissipator_operators. + rotating_frame: Rotating frame in which calcualtions are to be done. If provided, it is + assumed that all operators were already in the frame basis. + in_frame_basis: Whether to represent the model in the basis in which the rotating + frame operator is diagonalized. + evaluation_mode: Evaluation mode to use. Call ``help(LindbladModel.evaluation_mode)`` + for more details. + validate: If True check input hamiltonian_operators and static_hamiltonian are + Hermitian. + + Raises: + QiskitError: If model insufficiently or incorrectly specified. + """ + + if ( + static_hamiltonian is None + and hamiltonian_operators is None + and static_dissipators is None + and dissipator_operators is None + ): + raise QiskitError( + f"{type(self).__name__} requires at least one of static_hamiltonian " + "hamiltonian_operators, static_dissipators, or dissipator_operators " + "to be specified at construction." + ) + + static_hamiltonian = to_numeric_matrix_type(static_hamiltonian) + hamiltonian_operators = to_numeric_matrix_type(hamiltonian_operators) + if validate: + if (hamiltonian_operators is not None) and (not is_hermitian(hamiltonian_operators)): + raise QiskitError("""LinbladModel hamiltonian_operators must be Hermitian.""") + if (static_hamiltonian is not None) and (not is_hermitian(static_hamiltonian)): + raise QiskitError("""LinbladModel static_hamiltonian must be Hermitian.""") + + self._operator_collection = construct_lindblad_operator_collection( + evaluation_mode=evaluation_mode, + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=hamiltonian_operators, + static_dissipators=static_dissipators, + dissipator_operators=dissipator_operators, + ) + self._evaluation_mode = evaluation_mode + self.vectorized_operators = "vectorized" in evaluation_mode + + self._rotating_frame = None + self.rotating_frame = rotating_frame + self._in_frame_basis = None + self.in_frame_basis = in_frame_basis + + self.signals = (hamiltonian_signals, dissipator_signals) + +
+[docs] + @classmethod + def from_hamiltonian( + cls, + hamiltonian: HamiltonianModel, + static_dissipators: Optional[Union[Array, csr_matrix]] = None, + dissipator_operators: Optional[Union[Array, List[csr_matrix]]] = None, + dissipator_signals: Optional[Union[List[Signal], SignalList]] = None, + evaluation_mode: Optional[str] = None, + ): + """Construct from a :class:`HamiltonianModel`. + + Args: + hamiltonian: The :class:`HamiltonianModel`. + static_dissipators: List of dissipators with coefficient 1. + dissipator_operators: List of dissipators with time-dependent coefficients. + dissipator_signals: List time-dependent coefficients for dissipator_operators. + evaluation_mode: Evaluation mode. Call ``help(LindbladModel.evaluation_mode)`` for more + information. + + Returns: + LindbladModel: Linblad model from parameters. + """ + + if evaluation_mode is None: + evaluation_mode = hamiltonian.evaluation_mode + + ham_copy = hamiltonian.copy() + ham_copy.rotating_frame = None + + return cls( + static_hamiltonian=ham_copy._get_static_operator(in_frame_basis=False), + hamiltonian_operators=ham_copy._get_operators(in_frame_basis=False), + hamiltonian_signals=ham_copy.signals, + static_dissipators=static_dissipators, + dissipator_operators=dissipator_operators, + dissipator_signals=dissipator_signals, + rotating_frame=hamiltonian.rotating_frame, + in_frame_basis=hamiltonian.in_frame_basis, + evaluation_mode=evaluation_mode, + )
+ + + @property + def dim(self) -> int: + """The matrix dimension.""" + if self._operator_collection.static_hamiltonian is not None: + return self._operator_collection.static_hamiltonian.shape[-1] + elif self._operator_collection.hamiltonian_operators is not None: + return self._operator_collection.hamiltonian_operators[0].shape[-1] + elif self._operator_collection.static_dissipators is not None: + return self._operator_collection.static_dissipators[0].shape[-1] + else: + return self._operator_collection.dissipator_operators[0].shape[-1] + + @property + def signals(self) -> Tuple[SignalList]: + """The model's signals as tuple with the 0th entry storing the Hamiltonian signals and the + 1st entry storing the dissipator signals. + + Raises: + QiskitError: If, when setting this property, the given signals are incompatible with + operator structure. + """ + return (self._hamiltonian_signals, self._dissipator_signals) + + @signals.setter + def signals(self, new_signals: Tuple[List[Signal]]): + hamiltonian_signals, dissipator_signals = new_signals + + # set Hamiltonian signals + if hamiltonian_signals is None: + self._hamiltonian_signals = None + elif hamiltonian_signals is not None and self.hamiltonian_operators is None: + raise QiskitError("Hamiltonian signals must be None if hamiltonian_operators is None.") + else: + # if signals is a list, instantiate a SignalList + if isinstance(hamiltonian_signals, list): + hamiltonian_signals = SignalList(hamiltonian_signals) + + # if it isn't a SignalList by now, raise an error + if not isinstance(hamiltonian_signals, SignalList): + raise QiskitError("Hamiltonian signals specified in unaccepted format.") + + # verify signal length is same as operators + if isinstance(self.hamiltonian_operators, list): + len_hamiltonian_operators = len(self.hamiltonian_operators) + else: + len_hamiltonian_operators = self.hamiltonian_operators.shape[0] + if len(hamiltonian_signals) != len_hamiltonian_operators: + raise QiskitError( + "Hamiltonian signals need to have the same length as Hamiltonian operators." + ) + + self._hamiltonian_signals = hamiltonian_signals + + # set dissipator signals + if dissipator_signals is None: + self._dissipator_signals = None + elif dissipator_signals is not None and self.dissipator_operators is None: + raise QiskitError("Dissipator signals must be None if dissipator_operators is None.") + else: + # if signals is a list, instantiate a SignalList + if isinstance(dissipator_signals, list): + dissipator_signals = SignalList(dissipator_signals) + + # if it isn't a SignalList by now, raise an error + if not isinstance(dissipator_signals, SignalList): + raise QiskitError("Dissipator signals specified in unaccepted format.") + + # verify signal length is same as operators + if isinstance(self.dissipator_operators, list): + len_dissipator_operators = len(self.dissipator_operators) + else: + len_dissipator_operators = self.dissipator_operators.shape[0] + if len(dissipator_signals) != len_dissipator_operators: + raise QiskitError( + "Dissipator signals need to have the same length as dissipator operators." + ) + + self._dissipator_signals = dissipator_signals + + @property + def in_frame_basis(self) -> bool: + """Whether to represent the model in the basis in which the frame operator is + diagonalized. + """ + return self._in_frame_basis + + @in_frame_basis.setter + def in_frame_basis(self, in_frame_basis: bool): + self._in_frame_basis = in_frame_basis + + @property + def static_hamiltonian(self) -> Array: + """The static Hamiltonian term.""" + return self._get_static_hamiltonian(in_frame_basis=self._in_frame_basis) + + @static_hamiltonian.setter + def static_hamiltonian(self, static_hamiltonian: Array): + self._set_static_hamiltonian( + new_static_hamiltonian=static_hamiltonian, operator_in_frame_basis=self._in_frame_basis + ) + + @property + def hamiltonian_operators(self) -> Array: + """The Hamiltonian operators.""" + return self._get_hamiltonian_operators(in_frame_basis=self._in_frame_basis) + + @property + def static_dissipators(self) -> Array: + """The static dissipator operators.""" + return self._get_static_dissipators(in_frame_basis=self._in_frame_basis) + + @property + def dissipator_operators(self) -> Array: + """The dissipator operators.""" + return self._get_dissipator_operators(in_frame_basis=self._in_frame_basis) + + def _get_static_hamiltonian(self, in_frame_basis: Optional[bool] = False) -> Array: + """Get the constant Hamiltonian term. + + Args: + in_frame_basis: Flag for whether the returned ``static_operator`` should be in the basis + in which the frame is diagonal. + + Returns: + The static operator term. + """ + op = self._operator_collection.static_hamiltonian + if not in_frame_basis and self.rotating_frame is not None: + return self.rotating_frame.operator_out_of_frame_basis(op) + else: + return op + + def _set_static_hamiltonian( + self, + new_static_hamiltonian: Array, + operator_in_frame_basis: Optional[bool] = False, + ): + """Set the constant Hamiltonian term. + + Note that if the model has a rotating frame this will override any contributions to the + static term due to the frame transformation. + + Args: + new_static_hamiltonian: The static operator operator. + operator_in_frame_basis: Whether ``new_static_operator`` is already in the rotating + frame basis. + """ + if new_static_hamiltonian is None: + self._operator_collection.static_hamiltonian = None + else: + if not operator_in_frame_basis and self.rotating_frame is not None: + new_static_hamiltonian = self.rotating_frame.operator_into_frame_basis( + new_static_hamiltonian + ) + + self._operator_collection.static_hamiltonian = new_static_hamiltonian + + def _get_hamiltonian_operators(self, in_frame_basis: Optional[bool] = False) -> Tuple[Array]: + """Get the Hamiltonian operators, either in the frame basis or not. + + Args: + in_frame_basis: Whether to return in frame basis or not. + + Returns: + Hamiltonian operators. + """ + ham_ops = self._operator_collection.hamiltonian_operators + if not in_frame_basis and self.rotating_frame is not None: + ham_ops = self.rotating_frame.operator_out_of_frame_basis(ham_ops) + + return ham_ops + + def _get_static_dissipators(self, in_frame_basis: Optional[bool] = False) -> Tuple[Array]: + """Get the static dissipators, either in the frame basis or not. + + Args: + in_frame_basis: Whether to return in frame basis or not. + + Returns: + Dissipator operators. + """ + diss_ops = self._operator_collection.static_dissipators + if not in_frame_basis and self.rotating_frame is not None: + diss_ops = self.rotating_frame.operator_out_of_frame_basis(diss_ops) + + return diss_ops + + def _get_dissipator_operators(self, in_frame_basis: Optional[bool] = False) -> Tuple[Array]: + """Get the dissipator operators, either in the frame basis or not. + + Args: + in_frame_basis: Whether to return in frame basis or not. + + Returns: + Dissipator operators. + """ + diss_ops = self._operator_collection.dissipator_operators + if not in_frame_basis and self.rotating_frame is not None: + diss_ops = self.rotating_frame.operator_out_of_frame_basis(diss_ops) + + return diss_ops + + @property + def evaluation_mode(self) -> str: + """Numerical evaluation mode of the model. + + Available options: + + * ``'dense'``: Stores Hamiltonian and dissipator terms as dense Array types. + * ``'dense_vectorized'``: Stores the Hamiltonian and dissipator terms as + :math:`(dim^2,dim^2)` matrices that acts on a vectorized density matrix by + left-multiplication. Allows for direct evaluate generator. + * ``'sparse'``: Like dense, but matrices stored in sparse format. If the default Array + backend is JAX, uses JAX BCOO sparse arrays, otherwise uses scipy :class:`csr_matrix` + sparse type. + * ```sparse_vectorized```: Like dense_vectorized, but matrices stored in sparse format. If + the default Array backend is JAX, uses JAX BCOO sparse arrays, otherwise uses scipy + :class:`csr_matrix` sparse type. Note that JAX sparse mode is only recommended for use + on CPU. + + Raises: + NotImplementedError: If this property is set to something other than one of the above + modes. + """ + return self._evaluation_mode + + @evaluation_mode.setter + def evaluation_mode(self, new_mode: str): + if new_mode != self._evaluation_mode: + self._operator_collection = construct_lindblad_operator_collection( + evaluation_mode=new_mode, + static_hamiltonian=self._operator_collection.static_hamiltonian, + hamiltonian_operators=self._operator_collection.hamiltonian_operators, + static_dissipators=self._operator_collection.static_dissipators, + dissipator_operators=self._operator_collection.dissipator_operators, + ) + + self.vectorized_operators = "vectorized" in new_mode + self._evaluation_mode = new_mode + + @property + def rotating_frame(self): + """The rotating frame. + + This property can be set with a :class:`RotatingFrame` instance, or any valid constructor + argument to this class. + + Setting this property updates the internal operator to use the new frame. + """ + return self._rotating_frame + + @rotating_frame.setter + def rotating_frame(self, rotating_frame: Union[Operator, Array, RotatingFrame]): + new_frame = RotatingFrame(rotating_frame) + + # convert static hamiltonian to new frame setup + static_ham = self._get_static_hamiltonian(in_frame_basis=True) + if static_ham is not None: + static_ham = -1j * static_ham + + new_static_hamiltonian = transfer_static_operator_between_frames( + static_ham, + new_frame=new_frame, + old_frame=self.rotating_frame, + ) + + if new_static_hamiltonian is not None: + new_static_hamiltonian = 1j * new_static_hamiltonian + + # convert hamiltonian operators and dissipator operators + ham_ops = self._get_hamiltonian_operators(in_frame_basis=True) + static_diss_ops = self._get_static_dissipators(in_frame_basis=True) + diss_ops = self._get_dissipator_operators(in_frame_basis=True) + + new_hamiltonian_operators = transfer_operators_between_frames( + ham_ops, + new_frame=new_frame, + old_frame=self.rotating_frame, + ) + new_static_dissipators = transfer_operators_between_frames( + static_diss_ops, + new_frame=new_frame, + old_frame=self.rotating_frame, + ) + new_dissipator_operators = transfer_operators_between_frames( + diss_ops, + new_frame=new_frame, + old_frame=self.rotating_frame, + ) + + self._rotating_frame = new_frame + + self._operator_collection = construct_lindblad_operator_collection( + evaluation_mode=self.evaluation_mode, + static_hamiltonian=new_static_hamiltonian, + hamiltonian_operators=new_hamiltonian_operators, + static_dissipators=new_static_dissipators, + dissipator_operators=new_dissipator_operators, + ) + +
+[docs] + def evaluate_hamiltonian(self, time: float) -> Array: + """Evaluates Hamiltonian matrix at a given time. + + Args: + time: The time at which to evaluate the Hamiltonian. + + Returns: + Array: Hamiltonian matrix. + """ + + hamiltonian_sig_vals = None + if self._hamiltonian_signals is not None: + hamiltonian_sig_vals = self._hamiltonian_signals(time) + + ham = self._operator_collection.evaluate_hamiltonian(hamiltonian_sig_vals) + if self.rotating_frame.frame_diag is not None: + ham = self.rotating_frame.operator_into_frame( + time, + ham, + operator_in_frame_basis=True, + return_in_frame_basis=self._in_frame_basis, + vectorized_operators=self.vectorized_operators, + ) + + return ham
+ + +
+[docs] + def evaluate(self, time: float) -> Array: + """Evaluate the model in array format as a matrix, independent of state. + + Args: + time: The time to evaluate the model at. + + Returns: + Array: The evaluated model as an anti-Hermitian matrix. + + Raises: + QiskitError: If model cannot be evaluated. + NotImplementedError: If the model is currently unvectorized. + """ + hamiltonian_sig_vals = None + if self._hamiltonian_signals is not None: + hamiltonian_sig_vals = self._hamiltonian_signals(time) + elif self._operator_collection.hamiltonian_operators is not None: + raise QiskitError( + f"{type(self).__name__} with non-empty hamiltonian operators cannot be evaluated " + "without hamiltonian signals." + ) + + dissipator_sig_vals = None + if self._dissipator_signals is not None: + dissipator_sig_vals = self._dissipator_signals(time) + elif self._operator_collection.dissipator_operators is not None: + raise QiskitError( + f"{type(self).__name__} with non-empty dissipator operators cannot be evaluated " + "without dissipator signals." + ) + + if self.vectorized_operators: + out = self._operator_collection.evaluate(hamiltonian_sig_vals, dissipator_sig_vals) + return self.rotating_frame.vectorized_map_into_frame( + time, out, operator_in_frame_basis=True, return_in_frame_basis=self._in_frame_basis + ) + else: + raise NotImplementedError( + "Non-vectorized Lindblad models cannot be represented without a given state." + )
+ + +
+[docs] + def evaluate_rhs(self, time: Union[float, int], y: Array) -> Array: + """Evaluates the Lindblad model at a given time. + + Args: + time: The time at which the model should be evaluated. + y: Density matrix as an (n,n) Array if not using a vectorized evaluation_mode, or an + (n^2) Array if using vectorized evaluation. + + Returns: + Array: Either the evaluated generator or the state. + + Raises: + QiskitError: If signals not sufficiently specified. + """ + + hamiltonian_sig_vals = None + if self._hamiltonian_signals is not None: + hamiltonian_sig_vals = self._hamiltonian_signals(time) + elif self._operator_collection.hamiltonian_operators is not None: + raise QiskitError( + f"{type(self).__name__} with non-empty hamiltonian operators cannot be evaluated " + "without hamiltonian signals." + ) + + dissipator_sig_vals = None + if self._dissipator_signals is not None: + dissipator_sig_vals = self._dissipator_signals(time) + elif self._operator_collection.dissipator_operators is not None: + raise QiskitError( + f"{type(self).__name__} with non-empty dissipator operators cannot be evaluated " + "without dissipator signals." + ) + + if self.rotating_frame.frame_diag is not None: + # Take y out of the frame, but keep in the frame basis + rhs = self.rotating_frame.operator_out_of_frame( + time, + y, + operator_in_frame_basis=self._in_frame_basis, + return_in_frame_basis=True, + vectorized_operators=self.vectorized_operators, + ) + + rhs = self._operator_collection.evaluate_rhs( + hamiltonian_sig_vals, dissipator_sig_vals, rhs + ) + + # Put rhs back into the frame, potentially converting its basis. + rhs = self.rotating_frame.operator_into_frame( + time, + rhs, + operator_in_frame_basis=True, + return_in_frame_basis=self._in_frame_basis, + vectorized_operators=self.vectorized_operators, + ) + + else: + rhs = self._operator_collection.evaluate_rhs( + hamiltonian_sig_vals, dissipator_sig_vals, y + ) + + return rhs
+
+ + + +def construct_lindblad_operator_collection( + evaluation_mode: str, + static_hamiltonian: Union[None, Array, csr_matrix], + hamiltonian_operators: Union[None, Array, List[csr_matrix]], + static_dissipators: Union[None, Array, csr_matrix], + dissipator_operators: Union[None, Array, List[csr_matrix]], +) -> BaseLindbladOperatorCollection: + """Construct a Lindblad operator collection. + + Args: + evaluation_mode: String specifying new mode. Available options are ``'dense'``, + ``'sparse'``, ``'dense_vectorized'``, and ``'sparse_vectorized'``. + static_hamiltonian: Constant part of the Hamiltonian. + hamiltonian_operators: Operators in Hamiltonian with time-dependent coefficients. + static_dissipators: Dissipation operators with coefficient 1. + dissipator_operators: Dissipation operators with variable coefficients. + + Returns: + BaseLindbladOperatorCollection: Right-hand side evaluation object. + + Raises: + NotImplementedError: If ``evaluation_mode`` is not one of the above supported evaluation + modes. + """ + + # raise warning if sparse mode set with JAX not on cpu + if ( + Array.default_backend() == "jax" + and "sparse" in evaluation_mode + and jax.default_backend() != "cpu" + ): + warn( + """Using sparse mode with JAX is primarily recommended for use on CPU.""", + stacklevel=2, + ) + + if evaluation_mode == "dense": + CollectionClass = DenseLindbladCollection + elif evaluation_mode == "sparse": + if Array.default_backend() == "jax": + CollectionClass = JAXSparseLindbladCollection + else: + CollectionClass = SparseLindbladCollection + elif evaluation_mode == "dense_vectorized": + CollectionClass = DenseVectorizedLindbladCollection + elif evaluation_mode == "sparse_vectorized": + if Array.default_backend() == "jax": + CollectionClass = JAXSparseVectorizedLindbladCollection + else: + CollectionClass = SparseVectorizedLindbladCollection + else: + raise NotImplementedError( + f"Evaluation mode '{evaluation_mode}' is not supported. See " + "help(LindbladModel.evaluation_mode) for available options." + ) + + return CollectionClass( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=hamiltonian_operators, + static_dissipators=static_dissipators, + dissipator_operators=dissipator_operators, + ) +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/models/rotating_frame.html b/stable/0.4/_modules/qiskit_dynamics/models/rotating_frame.html new file mode 100644 index 000000000..af9cf354e --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/models/rotating_frame.html @@ -0,0 +1,1034 @@ + + + + + + + + qiskit_dynamics.models.rotating_frame - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.models.rotating_frame

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2021.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Module for rotating frame handling classes.
+"""
+
+from typing import Union, List, Optional
+import numpy as np
+from scipy.sparse import issparse, csr_matrix
+
+from qiskit import QiskitError
+from qiskit.quantum_info.operators import Operator
+from qiskit.quantum_info.operators.predicates import is_hermitian_matrix
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.type_utils import to_array, to_BCOO, to_numeric_matrix_type
+
+try:
+    import jax.numpy as jnp
+    from jax.experimental import sparse as jsparse
+
+    jsparse_matmul = jsparse.sparsify(jnp.matmul)
+except ImportError:
+    pass
+
+
+
+[docs] +class RotatingFrame: + r"""Class for representing a rotation frame transformation. + + This class provides functionality for transforming various objects into or out-of a rotating + frame specified by an anti-Hermitian operator :math:`F = -iH`. For example: + + * Bringing a "state" into/out of the frame: :math:`t, y \mapsto e^{\mp tF}y` + * Bringing an "operator" into/out of the frame: :math:`t, A \mapsto e^{\mp tF}Ae^{\pm tF}` + * Bringing a generator for a LMDE into/out of the frame: + :math:`t, G \mapsto e^{\mp tF}Ge^{\pm tF} - F` + + This class also contains functions for bringing states/operators into/out of the basis in which + :math:`F` is diagonalized, which we refer to as the "frame basis". All previously mentioned + functions also include optional arguments specifying whether the input/output are meant to be in + the frame basis. + + .. note:: + :class:`~qiskit_dynamics.models.RotatingFrame` can be instantiated + with a 1d array, which is understood to correspond to the diagonal entries + of a diagonal :math:`H` or :math:`F = -i H`. + + """ + + def __init__( + self, frame_operator: Union[Array, Operator], atol: float = 1e-10, rtol: float = 1e-10 + ): + """Initialize with a frame operator. + + Args: + frame_operator: The frame operator, which must be either Hermitian or anti-Hermitian. + atol: Absolute tolerance when verifying that the ``frame_operator`` is Hermitian or + anti-Hermitian. + rtol: Relative tolerance when verifying that the ``frame_operator`` is Hermitian or + anti-Hermitian. + """ + if isinstance(frame_operator, RotatingFrame): + frame_operator = frame_operator.frame_operator + + self._frame_operator = frame_operator + frame_operator = to_array(frame_operator) + + if frame_operator is None: + self._dim = None + self._frame_diag = None + self._frame_basis = None + self._frame_basis_adjoint = None + # if frame_operator is a 1d array, assume already diagonalized + elif frame_operator.ndim == 1: + # verify Hermitian or anti-Hermitian + # if Hermitian convert to anti-Hermitian + frame_operator = _enforce_anti_herm(frame_operator, atol=atol, rtol=rtol) + + self._frame_diag = Array(frame_operator) + self._frame_basis = None + self._frame_basis_adjoint = None + self._dim = len(self._frame_diag) + # if not, diagonalize it + else: + # verify Hermitian or anti-Hermitian + # if Hermitian convert to anti-Hermitian + frame_operator = _enforce_anti_herm(frame_operator, atol=atol, rtol=rtol) + + # diagonalize with eigh, utilizing assumption of anti-hermiticity + frame_diag, frame_basis = np.linalg.eigh(1j * frame_operator) + + self._frame_diag = Array(-1j * frame_diag) + self._frame_basis = Array(frame_basis) + self._frame_basis_adjoint = frame_basis.conj().transpose() + self._dim = len(self._frame_diag) + + # lazily evaluate change-of-basis matrices for vectorized operators. + self._vectorized_frame_basis = None + self._vectorized_frame_basis_adjoint = None + + @property + def dim(self) -> int: + """The dimension of the frame.""" + return self._dim + + @property + def frame_operator(self) -> Array: + """The original frame operator.""" + return self._frame_operator + + @property + def frame_diag(self) -> Array: + """The diagonal of the frame operator.""" + return self._frame_diag + + @property + def frame_basis(self) -> Array: + """The array containing diagonalizing unitary.""" + return self._frame_basis + + @property + def frame_basis_adjoint(self) -> Array: + """The adjoint of the diagonalizing unitary.""" + return self._frame_basis_adjoint + +
+[docs] + def state_into_frame_basis(self, y: Array) -> Array: + r"""Take a state into the frame basis, i.e. return ``self.frame_basis_adjoint @ y``. + + Args: + y: The state. + + Returns: + Array: The state in the frame basis. + """ + y = to_numeric_matrix_type(y) + if self.frame_basis_adjoint is None: + return y + + return self.frame_basis_adjoint @ y
+ + +
+[docs] + def state_out_of_frame_basis(self, y: Array) -> Array: + r"""Take a state out of the frame basis, i.e. ``return self.frame_basis @ y``. + + Args: + y: The state. + + Returns: + Array: The state in the frame basis. + """ + y = to_numeric_matrix_type(y) + if self.frame_basis is None: + return y + + return self.frame_basis @ y
+ + +
+[docs] + def operator_into_frame_basis( + self, + op: Union[Operator, List[Operator], Array, csr_matrix, None], + convert_type: bool = True, + ) -> Array: + r"""Take an operator into the frame basis, i.e. return + ``self.frame_basis_adjoint @ A @ self.frame_basis`` + + Args: + op: The operator or array of operators. + convert_type: Whether or not to initially convert ``op`` into an expected type. Should + only be set to ``False`` in situations in which it is gauranteed that + ``op`` is a handled input type. + + Returns: + Array: The operator in the frame basis. + """ + if convert_type: + op = to_numeric_matrix_type(op) + + if self.frame_basis is None or op is None: + return op + + if isinstance(op, list): + return [self.operator_into_frame_basis(x, convert_type=False) for x in op] + elif type(op).__name__ == "BCOO": + return self.frame_basis_adjoint @ jsparse_matmul(op, self.frame_basis.data) + else: + # parentheses are necessary for sparse op evaluation + return self.frame_basis_adjoint @ (op @ self.frame_basis)
+ + +
+[docs] + def operator_out_of_frame_basis( + self, + op: Union[Operator, List[Operator], Array, csr_matrix, None], + convert_type: bool = True, + ) -> Array: + r"""Take an operator out of the frame basis, i.e. return + ``self.frame_basis @ to_array(op) @ self.frame_basis_adjoint``. + + Args: + op: The operator or array of operators. + convert_type: Whether or not to initially convert ``op`` into an expected type. Should + only be set to ``False`` in situations in which it is gauranteed that + that ``op`` is a handled input type. + + Returns: + Array: The operator in the frame basis. + """ + if convert_type: + op = to_numeric_matrix_type(op) + + if self.frame_basis is None or op is None: + return op + + if isinstance(op, list): + return [self.operator_out_of_frame_basis(x, convert_type=False) for x in op] + elif type(op).__name__ == "BCOO": + return self.frame_basis @ jsparse_matmul(op, self.frame_basis_adjoint.data) + else: + # parentheses are necessary for sparse op evaluation + return self.frame_basis @ (op @ self.frame_basis_adjoint)
+ + +
+[docs] + def state_into_frame( + self, + t: float, + y: Array, + y_in_frame_basis: Optional[bool] = False, + return_in_frame_basis: Optional[bool] = False, + ): + """Take a state into the rotating frame, i.e. return ``exp(-tF) @ y``. + + Args: + t: The time. + y: The state. + y_in_frame_basis: Whether or not the array y is already in the basis in which the frame + is diagonal. + return_in_frame_basis: Whether or not to return the result in the frame basis. + + Returns: + Array: The state in the rotating frame. + """ + y = to_numeric_matrix_type(y) + if self._frame_operator is None: + return y + + out = y + + # if not in frame basis convert it + if not y_in_frame_basis: + out = self.state_into_frame_basis(out) + + # go into the frame using fast diagonal matrix multiplication + out = (np.exp(self.frame_diag * (-t)) * out.transpose()).transpose() # = e^{tF}out + + # if output is requested to not be in the frame basis, convert it + if not return_in_frame_basis: + out = self.state_out_of_frame_basis(out) + + return out
+ + +
+[docs] + def state_out_of_frame( + self, + t: float, + y: Array, + y_in_frame_basis: Optional[bool] = False, + return_in_frame_basis: Optional[bool] = False, + ) -> Array: + r"""Take a state out of the rotating frame, i.e. return ``exp(tF) @ y``. + + Calls ``self.state_into_frame`` with time reversed. + + Args: + t: The time. + y: The state.. + y_in_frame_basis: Whether or not the array y is already in the basis in which the frame + is diagonal. + return_in_frame_basis: Whether or not to return the result in the frame basis. + + Returns: + Array: The state out of the rotating frame. + """ + return self.state_into_frame(-t, y, y_in_frame_basis, return_in_frame_basis)
+ + + def _conjugate_and_add( + self, + t: float, + operator: Union[Array, csr_matrix], + op_to_add_in_fb: Optional[Array] = None, + operator_in_frame_basis: Optional[bool] = False, + return_in_frame_basis: Optional[bool] = False, + vectorized_operators: Optional[bool] = False, + ) -> Union[Array, csr_matrix]: + r"""General helper function for computing :math:`\exp(-tF)G\exp(tF) + B`. + + Note: :math:`B` is added in the frame basis before any potential final change + out of the frame basis. + + Note that there are two conventions for passing multiple operators at the same time. For + evaluation with ``vectorized_operators=False``, these operators should be passed as ``(k, + dim, dim)`` array objects, with the :math:`i^{th}` operator being stored as the ``[i,:,:]`` + entry. For ``vectorized_operators = True``, these (vectorized) operators should be passed as + a ``(dim**2, k)`` array, with the :math:`i^{th}` vectorized operator stored as the ``[:,i]`` + entry. + + Args: + t: The time. + operator: The operator :math:`G`. + op_to_add_in_fb: The additional operator :math:`B`. + operator_in_fame_basis: Whether ``operator`` is already in the basis in which the frame + operator is diagonal. + vectorized_operators: Whether ``operator`` is passed as a vectorized, ``(dim**2,)`` + Array, rather than a ``(dim,dim)`` Array. + + Returns: + Array of the newly conjugated operator. + """ + operator = to_numeric_matrix_type(operator) + op_to_add_in_fb = to_numeric_matrix_type(op_to_add_in_fb) + + if vectorized_operators: + # If passing vectorized operator, undo vectorization temporarily + if self._frame_operator is None: + if op_to_add_in_fb is None: + return operator + else: + return operator + op_to_add_in_fb + if len(operator.shape) == 2: + operator = operator.T + operator = operator.reshape(operator.shape[:-1] + (self.dim, self.dim), order="F") + + if self._frame_operator is None: + if op_to_add_in_fb is None: + return operator + else: + if op_to_add_in_fb is not None: + if issparse(operator): + op_to_add_in_fb = csr_matrix(op_to_add_in_fb) + elif type(operator).__name__ == "BCOO": + op_to_add_in_fb = to_BCOO(op_to_add_in_fb) + + return operator + op_to_add_in_fb + + out = operator + + # if not in frame basis convert it + if not operator_in_frame_basis: + out = self.operator_into_frame_basis(out) + + # get frame transformation matrix in diagonal basis + # assumption that F is anti-Hermitian implies conjugation of + # diagonal gives inversion + exp_freq = np.exp(self.frame_diag * t) + frame_mat = exp_freq.conj().reshape(self.dim, 1) * exp_freq + if issparse(out): + out = out.multiply(frame_mat) + elif type(out).__name__ == "BCOO": + out = out * frame_mat.data + else: + out = frame_mat * out + + if op_to_add_in_fb is not None: + if issparse(out): + op_to_add_in_fb = csr_matrix(op_to_add_in_fb) + elif type(out).__name__ == "BCOO": + op_to_add_in_fb = to_BCOO(op_to_add_in_fb) + + out = out + op_to_add_in_fb + + # if output is requested to not be in the frame basis, convert it + if not return_in_frame_basis: + out = self.operator_out_of_frame_basis(out) + + if vectorized_operators: + # If a vectorized output is required, reshape correctly + out = out.reshape(out.shape[:-2] + (self.dim**2,), order="F") + if len(out.shape) == 2: + out = out.T + + return out + +
+[docs] + def operator_into_frame( + self, + t: float, + operator: Union[Operator, Array, csr_matrix], + operator_in_frame_basis: Optional[bool] = False, + return_in_frame_basis: Optional[bool] = False, + vectorized_operators: Optional[bool] = False, + ) -> Array: + r"""Bring an operator into the frame, i.e. return + ``exp(-tF) @ operator @ exp(tF)``. + + The default implementation is to use ``self._conjugate_and_add``. + + Args: + t: The time. + operator: An array of appropriate size. + operator_in_frame_basis: Whether or not the operator is already in the basis in which + the frame is diagonal. + return_in_frame_basis: Whether or not to return the result in the frame basis. + vectorized_operators: Whether ``operator`` is passed as a vectorized, + ``(dim**2,)`` Array, rather than a ``(dim,dim)`` Array. + + Returns: + Array: The operator in the rotating frame. + """ + return self._conjugate_and_add( + t, + operator, + operator_in_frame_basis=operator_in_frame_basis, + return_in_frame_basis=return_in_frame_basis, + vectorized_operators=vectorized_operators, + )
+ + +
+[docs] + def operator_out_of_frame( + self, + t: float, + operator: Union[Operator, Array, csr_matrix], + operator_in_frame_basis: Optional[bool] = False, + return_in_frame_basis: Optional[bool] = False, + vectorized_operators: Optional[bool] = False, + ): + r"""Bring an operator into the rotating frame, i.e. return + ``exp(tF) @ operator @ exp(-tF)``. + + The default implmentation is to use `self.operator_into_frame`. + + Args: + t: Time. + operator: An array of appropriate size. + operator_in_frame_basis: Whether or not the operator is already in the basis in which + the frame is diagonal. + return_in_frame_basis: Whether or not to return the result in the frame basis. + vectorized_operators: Whether ``operator`` is passed as a vectorized, ``(dim**2,)`` + Array, rather than a ``(dim,dim)`` Array. + + Returns: + Array: The operator out of the rotating frame. + """ + return self.operator_into_frame( + -t, + operator, + operator_in_frame_basis=operator_in_frame_basis, + return_in_frame_basis=return_in_frame_basis, + vectorized_operators=vectorized_operators, + )
+ + +
+[docs] + def generator_into_frame( + self, + t: float, + operator: Union[Operator, Array, csr_matrix], + operator_in_frame_basis: Optional[bool] = False, + return_in_frame_basis: Optional[bool] = False, + vectorized_operators: Optional[bool] = False, + ): + r"""Take an generator into the rotating frame, i.e. return + ``exp(-tF) @ operator @ exp(tF) - F``. + + The default implementation is to use :meth:`_conjugate_and_add`. + + Args: + t: Time. + operator: Generator (array of appropriate size). + operator_in_frame_basis: Whether or not the generator is already in the basis in which + the frame is diagonal. + return_in_frame_basis: Whether or not to return the result in the frame basis. + vectorized_operators: Whether ``operator`` is passed as a vectorized, ``(dim**2,)`` + Array, rather than a ``(dim,dim)`` Array. + + Returns: + Array: The generator in the rotating frame. + """ + if self.frame_operator is None: + return to_numeric_matrix_type(operator) + else: + # conjugate and subtract the frame diagonal + return self._conjugate_and_add( + t, + operator, + op_to_add_in_fb=-np.diag(self.frame_diag), + operator_in_frame_basis=operator_in_frame_basis, + return_in_frame_basis=return_in_frame_basis, + vectorized_operators=vectorized_operators, + )
+ + +
+[docs] + def generator_out_of_frame( + self, + t: float, + operator: Union[Operator, Array, csr_matrix], + operator_in_frame_basis: Optional[bool] = False, + return_in_frame_basis: Optional[bool] = False, + ) -> Array: + r"""Take an operator out of the frame using the generator transformaton rule, i.e. return + ``exp(tF) @ operator @ exp(-tF) + F``. + + The default implementation is to use ``self._conjugate_and_add``. + + Args: + t: The time + operator: Generator (array of appropriate size). + operator_in_frame_basis: Whether or not the operator is already in the basis in which + the frame is diagonal. + return_in_frame_basis: Whether or not to return the result in the frame basis. + + Returns: + Array: The generator out of the rotating frame. + """ + if self.frame_operator is None: + return to_numeric_matrix_type(operator) + else: + # conjugate and add the frame diagonal + return self._conjugate_and_add( + -t, + operator, + op_to_add_in_fb=Array(np.diag(self.frame_diag)), + operator_in_frame_basis=operator_in_frame_basis, + return_in_frame_basis=return_in_frame_basis, + )
+ + + @property + def vectorized_frame_basis(self): + """Lazily evaluated operator for mapping vectorized operators into the frame basis.""" + + if self.frame_basis is None: + return None + + if self._vectorized_frame_basis is None: + self._vectorized_frame_basis = np.kron(self.frame_basis.conj(), self.frame_basis) + self._vectorized_frame_basis_adjoint = self._vectorized_frame_basis.conj().transpose() + + return self._vectorized_frame_basis + + @property + def vectorized_frame_basis_adjoint(self): + """Lazily evaluated operator for mapping vectorized operators out of the frame basis.""" + + if self.frame_basis is None: + return None + + if self._vectorized_frame_basis_adjoint is None: + # trigger lazy evaluation of vectorized_frame_basis + # pylint: disable=pointless-statement + self.vectorized_frame_basis + + return self._vectorized_frame_basis_adjoint + +
+[docs] + def vectorized_map_into_frame( + self, + time: float, + op: Array, + operator_in_frame_basis: Optional[bool] = False, + return_in_frame_basis: Optional[bool] = False, + ) -> Array: + r"""Given an operator ``op`` of dimension ``dim**2`` assumed to represent vectorized linear + map in column stacking convention, returns: + + .. math:: + ((e^{tF})^T \otimes e^{-tF}) \times op \times ((e^{-tF})^T \otimes e^{tF}). + + Utilizes element-wise multiplication :math:`op \to \Delta\otimes\bar{\Delta} \odot op`, + where :math:`\Delta_{ij}=\exp((-d_i+d_j)t)` is the frame difference matrix, as well as + caches array :math:`\bar{C}\otimes C`, where ``C = self.frame_basis`` for future use. + + Args: + time: The time :math:`t`. + op: The ``(dim**2,dim**2)`` Array. + operator_in_frame_basis: Whether the operator is in the frame basis. + return_in_frame_basis: Whether the operator should be returned in the frame basis. + + Returns: + ``op`` in the frame. + """ + if self.frame_diag is not None: + # Put the vectorized operator into the frame basis + if not operator_in_frame_basis and self.frame_basis is not None: + op = self.vectorized_frame_basis_adjoint @ (op @ self.vectorized_frame_basis) + + expvals = np.exp(self.frame_diag * time) # = e^{td_i} = e^{it*Im(d_i)} + # = kron(e^{-it*Im(d_i)},e^{it*Im(d_i)}), but ~3x faster + temp_outer = (expvals.conj().reshape(self.dim, 1) * expvals).flatten() + delta_bar_otimes_delta = np.outer( + temp_outer.conj(), temp_outer + ) # = kron(delta.conj(),delta) but >3x faster + if issparse(op): + op = op.multiply(delta_bar_otimes_delta) + else: + op = delta_bar_otimes_delta * op # hadamard product + + if not return_in_frame_basis and self.frame_basis is not None: + op = self.vectorized_frame_basis @ (op @ self.vectorized_frame_basis_adjoint) + + return op
+
+ + + +def _enforce_anti_herm(mat: Array, atol: Optional[float] = 1e-10, rtol: Optional[float] = 1e-10): + r"""Given ``mat``, the logic of this function is: + - if ``mat`` is hermitian, return ``-1j * mat`` + - if ``mat`` is anti-hermitian, return ``mat`` + - otherwise: + - if ``mat.backend == 'jax'`` return ``jnp.inf * mat`` + - otherwise raise an error + + The main purpose of this function is to hide the pecularities of implementing the above logic in + a compileable way in ``jax``. + + Args: + mat: The array to check. + atol: The absolute tolerance. + rtol: The relative tolerance. + + Returns: + Array: Anti-hermitian version of ``mat``, if applicable. + + Raises: + ImportError: If the backend is jax and jax is not installed. + QiskitError: If ``mat`` is not Hermitian or anti-Hermitian. + """ + mat = to_array(mat) + mat = Array(mat, dtype=complex) + + if mat.backend == "jax": + from jax.lax import cond + + mat = mat.data + + if mat.ndim == 1: + # this function checks if pure imaginary. If yes it returns the + # array, otherwise it multiplies it by jnp.nan to raise an error + # Note: pathways in conditionals in jax cannot raise Exceptions + def anti_herm_conditional(b): + aherm_pred = jnp.allclose(b, -b.conj(), atol=atol, rtol=rtol) + return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) + + # Check if it is purely real, if not apply anti_herm_conditional + herm_pred = jnp.allclose(mat, mat.conj(), atol=atol, rtol=rtol) + return Array(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + else: + # this function checks if anti-hermitian, if yes returns the array, + # otherwise it multiplies it by jnp.nan + def anti_herm_conditional(b): + aherm_pred = jnp.allclose(b, -b.conj().transpose(), atol=atol, rtol=rtol) + return cond(aherm_pred, lambda A: A, lambda A: jnp.nan * A, b) + + # the following lines check if a is hermitian, otherwise it feeds + # it into the anti_herm_conditional + herm_pred = jnp.allclose(mat, mat.conj().transpose(), atol=atol, rtol=rtol) + return Array(cond(herm_pred, lambda A: -1j * A, anti_herm_conditional, mat)) + + else: + if mat.ndim == 1: + if np.allclose(mat, mat.conj(), atol=atol, rtol=rtol): + return -1j * mat + elif np.allclose(mat, -mat.conj(), atol=atol, rtol=rtol): + return mat + else: + if is_hermitian_matrix(mat, rtol=rtol, atol=atol): + return -1j * mat + elif is_hermitian_matrix(1j * mat, rtol=rtol, atol=atol): + return mat + + # raise error if execution has made it this far + raise QiskitError("""frame_operator must be either a Hermitian or anti-Hermitian matrix.""") +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/models/rotating_wave_approximation.html b/stable/0.4/_modules/qiskit_dynamics/models/rotating_wave_approximation.html new file mode 100644 index 000000000..e3259851f --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/models/rotating_wave_approximation.html @@ -0,0 +1,653 @@ + + + + + + + + qiskit_dynamics.models.rotating_wave_approximation - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.models.rotating_wave_approximation

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2021.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name, inconsistent-return-statements
+
+"""Functions for performing the Rotating Wave Approximation
+on Model classes."""
+
+
+from typing import List, Optional, Union
+import numpy as np
+from qiskit_dynamics.models import (
+    BaseGeneratorModel,
+    GeneratorModel,
+    HamiltonianModel,
+    LindbladModel,
+    RotatingFrame,
+)
+from qiskit_dynamics.signals import SignalSum, Signal, SignalList
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.type_utils import to_array
+
+
+
+[docs] +def rotating_wave_approximation( + model: BaseGeneratorModel, cutoff_freq: float, return_signal_map: Optional[bool] = False +) -> BaseGeneratorModel: + r"""Construct a new model by performing the rotating wave approximation with a given cutoff + frequency. The outputs of this function can be used in JAX-transformable functions, however this + function itself cannot (see below). + + Performs elementwise rotating wave approximation (RWA) with cutoff frequency ``cutoff_freq`` on + each operator in a model, returning a new model. The new model contains a modified list of + signal coefficients, and setting the optional argument ``return_signal_map=True`` results in + the additional return of the function ``f`` which maps the signals of the input model to those + of the output RWA model, such that the code blocks: + + .. code-block:: python + + model.signals = new_signals + rwa_model = rotating_wave_approximation(model, cutoff_freq) + + and + + .. code-block:: python + + rwa_model, f = rotating_wave_approximation(model, cutoff_freq, return_signal_map=True) + rwa_model.signals = f(new_signals) + + result in an ``rwa_model`` with the same updated signals. + + .. note:: + The ``rotating_wave_approximation`` function itself cannot be included in a function + to-be JAX-transformed, however the resulting model and ``signal_map`` can. For example, + the following function is **not** JAX-transformable: + + .. code-block:: python + + def function_with_rwa(t): + operators = ... + signals = ... + model = GeneratorModel(operators=operators, signals=signals) + rwa_model = rotating_wave_approximation(model, cutoff_freq) + return rwa_model(t) + + Whereas, defining: + + .. code-block:: python + + rwa_model, signal_map = rotating_wave_approximation(model, + cutoff_freq, + return_signal_map=True) + + The following function **is** JAX-transformable: + + .. code-block:: python + + def jax_transformable_func(t): + rwa_model_copy = rwa_model.copy() + rwa_model_copy.signals = signal_map(new_signals) + return rwa_model_copy(t) + + In this way, the outputs of ``rotating_wave_approximation`` can be used in JAX-transformable + functions, however ``rotating_wave_approximation`` itself cannot. + + + We now describe the formalism. When considering :math:`s_i(t) e^{-tF}G_ie^{tF}`, in the basis in + which :math:`F` is diagonal, the :math:`(j, k)` element of :math:`G_i` has effective frequency + :math:`\tilde\nu_{ijk}^\pm = \pm\nu_i + Im[-d_j+d_k]/2\pi`, where the :math:`\pm\nu_i` comes + from expressing :math:`s_i(t) = Re[a_i(t)e^{2\pi i\nu_i t}] = a_i(t)e^{i(2\pi\nu_i t+\phi_i)}/2 + + c.c.` and the other term comes from the rotating frame. Define :math:`G_i^\pm` the matrix + whose entries :math:`(G_i^\pm)_{jk}` are the entries of :math:`G_i` s.t. + :math:`|\nu_{ijk}^\pm|<\nu_*` for some cutoff frequency :math:`\nu_*`. Then, after the RWA, we + may write + + .. math:: + s_i(t)G_i \to G_i^+ a_ie^{i(2\pi \nu_i t+\phi_i)}/2 + + G_i^- \overline{a_i}e^{-i(2\pi \nu_i t+\phi_i)}/2. + + When we regroup these to use only the real components of the signal, we find that + + .. math:: + s_i(t)G_i \to s_i(t)(G_i^+ + G_i^-)/2 + s_i'(t)(iG_i^+-iG_i^-) + + where :math:`s_i'(t)` is a signal with the same frequency and amplitude as :math:`s_i`, but with + a phase shift of :math:`\phi_i - \pi/2`. + + Args: + model: The model to approximate. + cutoff_freq: The cutoff frequency for the approximation. + return_signal_map: Whether to also return the function for mapping pre-RWA signals to + post-RWA signals. + + Returns: + :class:`GeneratorModel` with twice as many terms, and, if ``return_signal_map``, + also the function ``f``. + + Raises: + ValueError: If the model has no signals. + """ + + n = model.dim + + frame_freqs = None + if model.rotating_frame is None or model.rotating_frame.frame_diag is None: + frame_freqs = np.zeros((n, n), dtype=complex) + else: + diag = model.rotating_frame.frame_diag + diff_matrix = np.broadcast_to(diag, (n, n)) - np.broadcast_to(diag, (n, n)).T + frame_freqs = diff_matrix.imag / (2 * np.pi) + + if model.rotating_frame.frame_diag is not None: + frame_shift = np.diag(model.rotating_frame.frame_diag) + if isinstance(model, (HamiltonianModel, LindbladModel)): + frame_shift = 1j * frame_shift + else: + frame_shift = np.zeros((n, n), dtype=complex) + + if isinstance(model, GeneratorModel): + if model.signals is None and model.operators is not None: + raise ValueError("Model must have nontrivial signals to perform the RWA.") + + cur_drift = to_array(model._get_static_operator(True)) + if cur_drift is not None: + cur_drift = cur_drift + frame_shift + rwa_drift = cur_drift * (abs(frame_freqs) < cutoff_freq).astype(int) + rwa_drift = model.rotating_frame.operator_out_of_frame_basis(rwa_drift) + else: + rwa_drift = None + + rwa_operators = get_rwa_operators( + to_array(model._get_operators(True)), + model.signals, + model.rotating_frame, + frame_freqs, + cutoff_freq, + ) + rwa_signals = get_rwa_signals(model.signals) + + # works for both GeneratorModel and HamiltonianModel + rwa_model = model.__class__( + static_operator=rwa_drift, + operators=rwa_operators, + signals=rwa_signals, + rotating_frame=model.rotating_frame, + in_frame_basis=model.in_frame_basis, + evaluation_mode=model.evaluation_mode, + ) + if return_signal_map: + return rwa_model, get_rwa_signals + return rwa_model + + elif isinstance(model, LindbladModel): + if model.signals[0] is None and model.hamiltonian_operators is not None: + raise ValueError("Model must have nontrivial Hamiltonian signals to perform the RWA.") + + if model.signals[1] is None and model.dissipator_operators is not None: + raise ValueError("Model must have nontrivial dissipator signals to perform the RWA.") + + # static hamiltonian part + cur_drift = to_array(model._get_static_hamiltonian(True)) + frame_shift + rwa_drift = cur_drift * (abs(frame_freqs) < cutoff_freq).astype(int) + rwa_drift = model.rotating_frame.operator_out_of_frame_basis(rwa_drift) + + # static dissipator part + cur_static_dis = to_array(model._get_static_dissipators(in_frame_basis=True)) + rwa_static_dis = None + if cur_static_dis is not None: + rwa_static_dis = [] + for op in cur_static_dis: + op = Array(op) + rwa_op = op * (abs(frame_freqs) < cutoff_freq).astype(int) + rwa_op = model.rotating_frame.operator_out_of_frame_basis(rwa_op) + rwa_static_dis.append(rwa_op) + + cur_ham_ops = to_array(model._get_hamiltonian_operators(in_frame_basis=True)) + cur_dis_ops = to_array(model._get_dissipator_operators(in_frame_basis=True)) + + cur_ham_sig, cur_dis_sig = model.signals + + rwa_ham_ops = get_rwa_operators( + cur_ham_ops, cur_ham_sig, model.rotating_frame, frame_freqs, cutoff_freq + ) + rwa_ham_sig = get_rwa_signals(cur_ham_sig) + + rwa_dis_ops = get_rwa_operators( + cur_dis_ops, cur_dis_sig, model.rotating_frame, frame_freqs, cutoff_freq + ) + + rwa_dis_sig = get_rwa_signals(cur_dis_sig) + + rwa_model = LindbladModel( + static_hamiltonian=rwa_drift, + hamiltonian_operators=rwa_ham_ops, + hamiltonian_signals=rwa_ham_sig, + static_dissipators=rwa_static_dis, + dissipator_operators=rwa_dis_ops, + dissipator_signals=rwa_dis_sig, + rotating_frame=model.rotating_frame, + in_frame_basis=model.in_frame_basis, + evaluation_mode=model.evaluation_mode, + ) + + if return_signal_map: + signal_translator = lambda a: (get_rwa_signals(a[0]), get_rwa_signals(a[1])) + return rwa_model, signal_translator + return rwa_model
+ + + +def get_rwa_operators( + current_ops: Array, + current_sigs: SignalList, + rotating_frame: RotatingFrame, + frame_freqs: Array, + cutoff_freq: float, +): + r"""Given a set of operators as a ``(k,n,n)`` array, a set of frequencies + :math:`\operatorname{frame_freqs}_{jk} = \operatorname{Im}[-d_j+d_k]` where :math:`d_i` the + :math:`i^{th}` eigenlvalue of the frame operator :math:`F`, the current signals of a model, and + a cutoff frequency, returns the new operators and signals that should be passed to create a new + Model class after the RWA. + + Args: + current_ops: The current operator list, a ``(k,n,n)`` array. + current_sigs: ``(k,)`` length :class:`SignalList`. + rotating_frame: The current :class:`~RotatingFrame` object of the pre-RWA model + frame_freqs: The effective frequencies of different matrix elements due to the conjugation + by :math:`e^{\pm Ft}` in the rotating frame. + cutoff_freq: The maximum frequency allowed under the RWA. + + Returns: + SignaLList: ``(2k,n,n)`` array of new operators post RWA. + """ + if current_ops is None: + return None + + current_sigs = current_sigs.flatten() + carrier_freqs = np.zeros(len(current_ops)) + + for i, sig_sum in enumerate(current_sigs.components): + sig = sig_sum.components[0] + carrier_freqs[i] = sig.carrier_freq + + num_components = len(carrier_freqs) + n = current_ops[0].shape[-1] + + frame_freqs = np.broadcast_to(frame_freqs, (num_components, n, n)) + carrier_freqs = carrier_freqs.reshape((num_components, 1, 1)) + + pos_pass = np.abs(carrier_freqs + frame_freqs) < cutoff_freq + pos_terms = current_ops * pos_pass.astype(int) # G_i^+ + + neg_pass = np.abs(-carrier_freqs + frame_freqs) < cutoff_freq + neg_terms = current_ops * neg_pass.astype(int) # G_i^- + + real_component = pos_terms / 2 + neg_terms / 2 + imag_component = 1j * pos_terms / 2 - 1j * neg_terms / 2 + + return rotating_frame.operator_out_of_frame_basis( + np.append(real_component, imag_component, axis=0) + ) + + +def get_rwa_signals(curr_signal_list: Union[List[Signal], SignalList]): + """Helper function that converts pre-RWA signals to post-RWA signals. + + Args: + curr_signal_list: The pre-RWA signals. + + Returns: + The post-RWA signals. + """ + if curr_signal_list is None: + return curr_signal_list + + real_signal_components = [] + imag_signal_components = [] + + if not isinstance(curr_signal_list, SignalList): + curr_signal_list = SignalList(curr_signal_list) + + curr_signal_list = curr_signal_list.flatten() + + for sig_sum in curr_signal_list.components: + sig = sig_sum.components[0] + real_signal_components.append(sig) + imag_signal_components.append( + SignalSum(Signal(sig.envelope, sig.carrier_freq, sig.phase - np.pi / 2)) + ) + + return SignalList(real_signal_components + imag_signal_components) +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/perturbation/array_polynomial.html b/stable/0.4/_modules/qiskit_dynamics/perturbation/array_polynomial.html new file mode 100644 index 000000000..435a01389 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/perturbation/array_polynomial.html @@ -0,0 +1,1280 @@ + + + + + + + + qiskit_dynamics.perturbation.array_polynomial - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.perturbation.array_polynomial

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2022.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""
+Array polynomial.
+"""
+
+from typing import List, Optional, Callable, Tuple, Union
+from copy import copy
+from itertools import product
+
+from multiset import Multiset
+
+import numpy as np
+from numpy.typing import DTypeLike
+
+from qiskit import QiskitError
+
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.perturbation.multiset_utils import (
+    _validate_non_negative_ints,
+    _get_all_submultisets,
+    _sorted_multisets,
+    _submultisets_and_complements,
+    _multiset_to_sorted_list,
+)
+from qiskit_dynamics.perturbation.custom_binary_op import _CustomBinaryOp
+
+try:
+    import jax.numpy as jnp
+except ImportError:
+    pass
+
+
+
+[docs] +class ArrayPolynomial: + r"""A polynomial with array-valued coefficients. + + This class represents a multi-variable function of the form: + + .. math:: + f(c_1, \dots, c_r) = A_\emptyset + \sum_{I \in S} c_I A_I, + + where in the above: + + - :math:`S` is a finite set of multisets + indicating non-zero monomial terms, + - For a given multiset of non-negative integers :math:`I=(i_1, \dots, i_k)`, + :math:`c_I = c_{i_1} \times \dots \times c_{i_k}`, and + - The :math:`A_I` are arrays of the same shape, indexed by the first dimension. + + See the :ref:`multiset and power series notation section <multiset power series>` + of the perturbation review for an explanation of the multiset notation. + + An :class:`.ArrayPolynomial` is instantiated with the arguments: + + - ``constant_term`` specifying the array :math:`A_\emptyset`. + - ``array_coefficients`` specifying a list of the arrays :math:`A_I`, or as a single array + whose first index lists the :math:`A_I`, + - ``monomial_labels`` specifying the set :math:`S` as a list of + ``Multiset`` instances ordered in + correspondence with ``array_coefficients``. + + For example, the :class:`.ArrayPolynomial` corresponding to the mathematical polynomial + + .. math:: + + f(c_0, c_1) = A_\emptyset + + c_{(0)} A_{(0)} + c_{(0, 1)}A_{(0, 1)} + c_{(1, 1)}A_{(1, 1)} + + for arrays :math:`A_\emptyset, A_{(0)}, A_{(0, 1)}, A_{(1, 1)}` stored in variables + ``A_c``, ``A0``, ``A01``, and ``A11`` can be instantiated with + + .. code-block:: python + + ap = ArrayPolynomial( + constant_term = A_c + array_coefficients=[A0, A01, A11], + monomial_labels=[Multiset({0: 1}), Multiset({0: 1, 1: 1}), Multiset({1: 2})] + ) + + Once instantiated, the polynomial can be evaluated on an array of variable values, e.g. + + .. code-block:: python + + c = np.array([c0, c1]) + ap(c) # polynomial evaluated on variables + + :class:`.ArrayPolynomial` supports some array properties, e.g. ``ap.shape`` and ``ap.ndim`` + return the shape and number of dimensions of the output of the polynomial. Some array + methods are also supported, such as ``transpose`` and ``trace``, and their output produces + a new :class:`.ArrayPolynomial` which evaluates to the array one would obtain by first + evaluating the original, then calling the array method. E.g. + + .. code-block:: python + + ap2 = ap1.transpose() + ap2(c) == ap1(c).transpose() + + Finally, :class:`.ArrayPolynomial` supports algebraic operations, e.g. + + .. code-block:: python + + ap3 = ap1 @ ap2 + ap3(c) == ap1(c) @ ap2(c) + + It also has specialized algebraic methods that perform algebraic operations while + "ignoring" terms. E.g., for two instances ``ap1`` and ``ap2``, the call + + .. code-block:: python + + ap1.matmul(ap2, monomial_filter=lambda x: len(x) <= 3) + + is similar to ``ap1 @ ap2``, but will result in an :class:`.ArrayPolynomial` in which all + terms of degree larger than ``3`` will not be included in the results. + """ + __array_priority__ = 20 + + def __init__( + self, + constant_term: Optional[Array] = None, + array_coefficients: Optional[Array] = None, + monomial_labels: Optional[List[Multiset]] = None, + ): + """Construct a multivariable matrix polynomial. + + Args: + constant_term: An array representing the constant term of the polynomial. + array_coefficients: A 3d array representing a list of array coefficients. + monomial_labels: A list of multisets with non-negative integer entries of the same + length as ``array_coefficients`` indicating the monomial coefficient + for each corresponding ``array_coefficients``. + Raises: + QiskitError: If insufficient information is supplied to define an ArrayPolynomial, + or if monomial labels contain anything other than non-negative integers. + """ + + if array_coefficients is None and constant_term is None: + raise QiskitError( + "At least one of array_coefficients and constant_term must be specified." + ) + + if monomial_labels is not None: + self._monomial_labels = [Multiset(m) for m in monomial_labels] + for m in self._monomial_labels: + _validate_non_negative_ints(m) + else: + self._monomial_labels = [] + + # If operating in jax mode, wrap in Arrays + if Array.default_backend() == "jax": + if array_coefficients is not None: + self._array_coefficients = Array(array_coefficients) + else: + self._array_coefficients = None + + if constant_term is not None: + self._constant_term = Array(constant_term) + else: + self._constant_term = None + + self._compute_monomials = _get_monomial_compute_function_jax(self._monomial_labels) + else: + if constant_term is not None: + self._constant_term = np.array(constant_term) + else: + self._constant_term = None + + if array_coefficients is not None: + self._array_coefficients = np.array(array_coefficients) + else: + self._array_coefficients = None + + self._compute_monomials = _get_monomial_compute_function(self._monomial_labels) + + @property + def monomial_labels(self) -> Union[List, None]: + """The monomial labels corresponding to non-constant terms.""" + return self._monomial_labels + + @property + def array_coefficients(self) -> Union[Array, None]: + """The array coefficients for non-constant terms.""" + return self._array_coefficients + + @property + def constant_term(self) -> Union[Array, None]: + """The constant term.""" + return self._constant_term + +
+[docs] + def compute_monomials(self, c: Array) -> Union[Array, None]: + """Vectorized computation of all scalar monomial terms in the polynomial as specified by + ``self.monomial_labels``. + + Args: + c: Array of variables. + Returns: + Array of all monomial terms ordered according to ``self.monomial_labels``. + """ + return self._compute_monomials(c)
+ + + @property + def shape(self) -> Tuple[int]: + """Shape of the arrays in the polynomial.""" + if self._constant_term is not None: + return self._constant_term.shape + else: + return self._array_coefficients.shape[1:] + + @property + def ndim(self) -> int: + """Number of dimensions of the coefficients of the polynomial.""" + if self._constant_term is not None: + return self._constant_term.ndim + else: + return self._array_coefficients.ndim - 1 + +
+[docs] + def conj(self) -> "ArrayPolynomial": + """Return an ArrayPolynomial that is the conjugate of self.""" + + constant_term = None + coefficients = None + + if self._constant_term is not None: + constant_term = np.conj(self._constant_term) + + if self._array_coefficients is not None: + coefficients = np.conj(self._array_coefficients) + + return ArrayPolynomial( + array_coefficients=coefficients, + monomial_labels=copy(self._monomial_labels), + constant_term=constant_term, + )
+ + +
+[docs] + def transpose(self, axes: Optional[Tuple[int]] = None) -> "ArrayPolynomial": + """Return the ArrayPolynomial when transposing all coefficients.""" + + constant_term = None + coefficients = None + + if self._constant_term is not None: + constant_term = np.transpose(self._constant_term, axes) + + if self._array_coefficients is not None: + if axes is None: + axes = tuple(range(1, self.ndim + 1)[::-1]) + else: + axes = tuple(ax + 1 for ax in axes) + axes = (0,) + axes + coefficients = np.transpose(self._array_coefficients, axes) + + return ArrayPolynomial( + array_coefficients=coefficients, + monomial_labels=copy(self._monomial_labels), + constant_term=constant_term, + )
+ + +
+[docs] + def trace( + self, + offset: Optional[int] = 0, + axis1: Optional[int] = 0, + axis2: Optional[int] = 1, + dtype: Optional[DTypeLike] = None, + ) -> "ArrayPolynomial": + """Take the trace of the coefficients.""" + + if self.ndim < 2: + raise QiskitError("ArrayPolynomial.trace() requires ArrayPolynomial.ndim at least 2.") + + constant_term = None + coefficients = None + + if self._constant_term is not None: + constant_term = np.trace( + self._constant_term, offset=offset, axis1=axis1, axis2=axis2, dtype=dtype + ) + + if self._array_coefficients is not None: + coefficients = np.trace( + self._array_coefficients, + offset=offset, + axis1=axis1 + 1, + axis2=axis2 + 1, + dtype=dtype, + ) + + return ArrayPolynomial( + array_coefficients=coefficients, + monomial_labels=copy(self._monomial_labels), + constant_term=constant_term, + )
+ + +
+[docs] + def sum(self, axis: Optional[Union[int, Tuple[int]]] = None, dtype: Optional[DTypeLike] = None): + """Perform a sum on the coefficients.""" + + constant_term = None + coefficients = None + + # constant term can be handled normally + if self.constant_term is not None: + constant_term = self.constant_term.sum(axis=axis, dtype=dtype) + + # axis must be shifted for array coefficients + if self.array_coefficients is not None: + if self.ndim == 0 and axis is None: + coefficients = np.array(self.array_coefficients, dtype=dtype) + else: + if axis is None: + axis = tuple(k for k in range(1, self.ndim + 1)) + elif isinstance(axis, int): + axis = axis + 1 + elif isinstance(axis, tuple): + axis = tuple(k + 1 for k in axis) + + coefficients = self.array_coefficients.sum(axis=axis, dtype=dtype) + + return ArrayPolynomial( + array_coefficients=coefficients, + monomial_labels=copy(self._monomial_labels), + constant_term=constant_term, + )
+ + + @property + def real(self) -> "ArrayPolynomial": + """Return the real part of self.""" + + constant_term = None + array_coefficients = None + + if self.constant_term is not None: + constant_term = self.constant_term.real + + if self.array_coefficients is not None: + array_coefficients = self.array_coefficients.real + + return ArrayPolynomial( + array_coefficients=array_coefficients, + monomial_labels=copy(self._monomial_labels), + constant_term=constant_term, + ) + +
+[docs] + def add( + self, + other: Union["ArrayPolynomial", int, float, complex, Array], + monomial_filter: Optional[Callable] = None, + ) -> "ArrayPolynomial": + """Add two polynomials with bounds on which terms to keep. + + Optionally, a function ``monomial_filter`` can be provided to limit which monomials + appear in the output. It must accept as input a ``Multiset`` and return a ``bool``, + and a term with label given by ``multiset`` will be included only if + ``monomial_filter(multiset) == True``, and will not be computed if + ``monomial_filter(multiset) == False``. + + Args: + other: Other to add to self. + monomial_filter: Function determining which terms to compute and keep. + Returns: + ArrayPolynomial achieved by adding both self and other. + Raises: + QiskitError: if other cannot be cast as an ArrayPolynomial. + """ + + if isinstance(other, (int, float, complex, np.ndarray, Array)): + other = ArrayPolynomial(constant_term=other) + + if isinstance(other, ArrayPolynomial): + return _array_polynomial_addition(self, other, monomial_filter=monomial_filter) + + raise QiskitError( + "Only types castable as an ArrayPolynomial can be added to an ArrayPolynomial." + )
+ + +
+[docs] + def matmul( + self, + other: Union["ArrayPolynomial", int, float, complex, np.ndarray, Array], + monomial_filter: Optional[Callable] = None, + ) -> "ArrayPolynomial": + """Matmul self @ other with bounds on which terms to keep. + + Optionally, a function ``monomial_filter`` can be provided to limit which monomials + appear in the output. It must accept as input a ``Multiset`` and return a ``bool``, + and a term with label given by ``multiset`` will be included only if + ``monomial_filter(multiset) == True``, and will not be computed if + ``monomial_filter(multiset) == False``. + + Args: + other: Other to add to self. + monomial_filter: Function determining which terms to compute and keep. + Returns: + ArrayPolynomial achieved by matmul of self and other. + Raises: + QiskitError: if other cannot be cast as an ArrayPolynomial. + """ + if isinstance(other, (int, float, complex, np.ndarray, Array)): + other = ArrayPolynomial(constant_term=other) + + if isinstance(other, ArrayPolynomial): + return _array_polynomial_distributive_binary_op( + self, other, lambda A, B: A @ B, monomial_filter=monomial_filter + ) + + raise QiskitError(f"Type {type(other)} not supported by ArrayPolynomial.matmul.")
+ + +
+[docs] + def mul( + self, + other: Union["ArrayPolynomial", int, float, complex, np.ndarray, Array], + monomial_filter: Optional[Callable] = None, + ) -> "ArrayPolynomial": + """Entrywise multiplication of two ArrayPolynomials with bounds on which terms to keep. + + Optionally, a function ``monomial_filter`` can be provided to limit which monomials + appear in the output. It must accept as input a ``Multiset`` and return a ``bool``, + and a term with label given by ``multiset`` will be included only if + ``monomial_filter(multiset) == True``, and will not be computed if + ``monomial_filter(multiset) == False``. + + Args: + other: Other to add to self. + monomial_filter: Function determining which terms to compute and keep. + Returns: + ArrayPolynomial achieved by matmul of self and other. + Raises: + QiskitError: if other cannot be cast as an ArrayPolynomial. + """ + + if isinstance(other, (int, float, complex, np.ndarray, Array)): + other = ArrayPolynomial(constant_term=other) + + if isinstance(other, ArrayPolynomial): + return _array_polynomial_distributive_binary_op( + self, other, lambda A, B: A * B, monomial_filter=monomial_filter + ) + + raise QiskitError(f"Type {type(other)} not supported by ArrayPolynomial.mul.")
+ + + def __add__( + self, other: Union["ArrayPolynomial", int, float, complex, Array] + ) -> "ArrayPolynomial": + """Dunder method for addition of two ArrayPolynomials.""" + return self.add(other) + + def __radd__( + self, other: Union["ArrayPolynomial", int, float, complex, Array] + ) -> "ArrayPolynomial": + """Dunder method for right-addition of two ArrayPolynomials.""" + return self.add(other) + + def __neg__(self) -> "ArrayPolynomial": + constant_term = None + if self.constant_term is not None: + # pylint: disable=invalid-unary-operand-type + constant_term = -self.constant_term + + array_coefficients = None + if self.array_coefficients is not None: + # pylint: disable=invalid-unary-operand-type + array_coefficients = -self.array_coefficients + + return ArrayPolynomial( + constant_term=constant_term, + monomial_labels=self.monomial_labels, + array_coefficients=array_coefficients, + ) + + def __sub__( + self, other: Union["ArrayPolynomial", int, float, complex, Array] + ) -> "ArrayPolynomial": + return self + (-other) + + def __rsub__( + self, other: Union["ArrayPolynomial", int, float, complex, Array] + ) -> "ArrayPolynomial": + return other + (-self) + + def __mul__( + self, other: Union["ArrayPolynomial", int, float, complex, Array] + ) -> "ArrayPolynomial": + """Dunder method for entry-wise multiplication.""" + return self.mul(other) + + def __rmul__( + self, other: Union["ArrayPolynomial", int, float, complex, Array] + ) -> "ArrayPolynomial": + """Dunder method for right-multiplication.""" + return self.mul(other) + + def __matmul__(self, other: Union["ArrayPolynomial", Array]) -> "ArrayPolynomial": + """Dunder method for matmul.""" + return self.matmul(other) + + def __rmatmul__(self, other: Union["ArrayPolynomial", Array]) -> "ArrayPolynomial": + """Dunder method for rmatmul.""" + if isinstance(other, (int, float, complex, np.ndarray, Array)): + other = ArrayPolynomial(constant_term=other) + + if isinstance(other, ArrayPolynomial): + return other.matmul(self) + + raise QiskitError(f"Type {type(other)} not supported by ArrayPolynomial.__rmatmul__.") + + def __getitem__(self, idx) -> "ArrayPolynomial": + """Index the ArrayPolynomial similarly to an array.""" + constant_term = None + array_coefficients = None + + if self.constant_term is not None: + constant_term = self.constant_term[idx] + + if self.array_coefficients is not None: + array_coefficients = self.array_coefficients[(slice(None),) + idx] + + return ArrayPolynomial( + array_coefficients=array_coefficients, + monomial_labels=copy(self._monomial_labels), + constant_term=constant_term, + ) + + def __len__(self) -> int: + """Number of terms in the polynomial.""" + num_terms = 0 + if self.array_coefficients is not None: + num_terms = num_terms + len(self.array_coefficients) + + if self.constant_term is not None: + num_terms = num_terms + 1 + + return num_terms + + def __call__(self, c: Optional[Array] = None) -> Array: + """Evaluate the polynomial. + + Args: + c: Array of variables. + Returns: + Value of the polynomial at c. + """ + + if self._array_coefficients is not None and self._constant_term is not None: + monomials = self._compute_monomials(c) + return self._constant_term + np.tensordot( + self._array_coefficients, monomials, axes=(0, 0) + ) + elif self._array_coefficients is not None: + monomials = self._compute_monomials(c) + return np.tensordot(self._array_coefficients, monomials, axes=(0, 0)) + else: + return self._constant_term
+ + + +def _get_monomial_compute_function(multisets: List[Multiset]) -> Callable: + """Construct a vectorized function for computing multivariable monomial terms indicated by + multisets. + + The returned function takes in the individual variables as an array, and returns an array + of computed monomial terms in the order indicated by multisets. + + The returned function is vectorized in the sense that the supplied first order terms can be + arrays. + + The algorithm computes monomial terms of increasing order, recursively utilizing lower + order terms. + + Args: + multisets: list of multisets. + + Returns: + Callable: Vectorized function for computing monomials. + """ + if multisets is None or len(multisets) == 0: + return lambda c: None + + complete_multiset_list = _get_all_submultisets(multisets) + + ( + first_order_terms, + first_order_range, + left_indices, + right_indices, + update_ranges, + ) = _get_recursive_monomial_rule(complete_multiset_list) + + multiset_len = len(complete_multiset_list) + + location_list = np.array( + [complete_multiset_list.index(multiset) for multiset in multisets], dtype=int + ) + + def monomial_function(c): + shape = [multiset_len] + list(c.shape[1:]) + mono_vec = np.empty(shape=shape, dtype=complex) + mono_vec[first_order_range[0] : first_order_range[1]] = c[first_order_terms] + + for left_index, right_index, update_range in zip( + left_indices, right_indices, update_ranges + ): + mono_vec[update_range[0] : update_range[1]] = ( + mono_vec[left_index] * mono_vec[right_index] + ) + + return mono_vec[location_list] + + return monomial_function + + +# pylint: disable=invalid-name +def _get_monomial_compute_function_jax(multisets: List) -> Callable: + """JAX version of _get_monomial_compute_function.""" + + if multisets is None or len(multisets) == 0: + return lambda c: None + + complete_multiset_list = _get_all_submultisets(multisets) + + first_order_terms, _, left_indices, right_indices, _ = _get_recursive_monomial_rule( + complete_multiset_list + ) + location_list = np.array( + [complete_multiset_list.index(multiset) for multiset in multisets], dtype=int + ) + + # initial function sets up required first order terms + def monomial_function_init(c): + return c[first_order_terms] + + monomial_function = monomial_function_init + + # function for generating next update and compositions to avoid looping reference issues + def get_next_update_func(left_index, right_index): + def update_next(mono_vec): + return jnp.append(mono_vec, mono_vec[left_index] * mono_vec[right_index], axis=0) + + return update_next + + def compose_functions(f, g): + def new_func(x): + return g(f(x)) + + return new_func + + # recursively compose updates with monomial_function + for left_index, right_index in zip(left_indices, right_indices): + next_update = get_next_update_func(left_index, right_index) + monomial_function = compose_functions(monomial_function, next_update) + + # return only the requested terms + def trimmed_output_function(c): + return monomial_function(c)[location_list] + + return trimmed_output_function + + +def _get_recursive_monomial_rule(complete_multisets: List) -> Tuple: + """Helper function for _get_monomial_compute_function and _get_monomial_compute_function_jax; + computes a representation of the algorithm for computing monomials that is used by both + functions. + + complete_multisets is assumed to be closed under taking submultisets and in canonical order. + + Args: + complete_multisets: Description of monomial terms. + + Returns: + Tuple: Collection of lists organizing computation for both + _get_monomial_compute_function and _get_monomial_compute_function_jax. + """ + + # first, construct representation of recursive rule explicitly in terms of multisets + first_order_terms = [] + left_terms = [] + right_terms = [] + current_left = -1 + current_right_list = [] + current_len = 2 + + # convert multisets to list representation + complete_multisets = [_multiset_to_sorted_list(multiset) for multiset in complete_multisets] + + for multiset in complete_multisets: + if len(multiset) == 1: + first_order_terms.append(multiset[0]) + else: + if multiset[0] != current_left or len(multiset) != current_len: + current_len = len(multiset) + if current_left != -1: + left_terms.append(current_left) + right_terms.append(current_right_list) + + current_left = multiset[0] + current_right_list = [multiset[1:]] + else: + current_right_list.append(multiset[1:]) + + # if current_left is still -1, then only first order terms exist + if current_left == -1: + return np.array(first_order_terms), [0, len(first_order_terms)], [], [], [] + + # proceed assuming at least one term above first order exists + # append the last one + left_terms.append(current_left) + right_terms.append(current_right_list) + + # convert representation of rule in terms of multisets into one in terms of + # array indices + + # set up arrays + first_order_terms = np.array(first_order_terms, dtype=int) + + left_indices = [] + right_indices = [] + for left_term, right_term in zip(left_terms, right_terms): + left_indices.append(complete_multisets.index([left_term])) + + right_index_list = [] + for term in right_term: + right_index_list.append(complete_multisets.index(term)) + + right_indices.append(np.array(right_index_list, dtype=int)) + + # set up index updating ranges + first_order_range = [0, len(first_order_terms)] + update_ranges = [] + current_idx = first_order_range[1] + for right_index in right_indices: + next_idx = current_idx + len(right_index) + update_ranges.append([current_idx, next_idx]) + current_idx = next_idx + + return ( + np.array(first_order_terms), + first_order_range, + np.array(left_indices), + right_indices, + update_ranges, + ) + + +def _array_polynomial_distributive_binary_op( + ap1: ArrayPolynomial, + ap2: ArrayPolynomial, + binary_op: Callable, + monomial_filter: Optional[Callable] = None, +) -> ArrayPolynomial: + """Apply a distributive binary op on two array polynomials.""" + + # determine list of Multisets required for monomial labels, including filtering + all_multisets = [] + + # if no filter is provided, set to always return True + if monomial_filter is None: + monomial_filter = lambda x: True + + if ap1.constant_term is not None: + for multiset in ap2.monomial_labels: + if monomial_filter(multiset) and multiset not in all_multisets: + all_multisets.append(multiset) + if ap2.constant_term is not None: + for multiset in ap1.monomial_labels: + if monomial_filter(multiset) and multiset not in all_multisets: + all_multisets.append(multiset) + + for I, J in product(ap1.monomial_labels, ap2.monomial_labels): + IuJ = I + J + if monomial_filter(IuJ) and IuJ not in all_multisets: + all_multisets.append(IuJ) + + all_multisets = _sorted_multisets(all_multisets) + + # setup constant term + new_constant_term = None + if ( + ap1.constant_term is not None + and ap2.constant_term is not None + and monomial_filter(Multiset({})) + ): + new_constant_term = binary_op(ap1.constant_term, ap2.constant_term) + + # return constant case + if not all_multisets: + return ArrayPolynomial(constant_term=new_constant_term) + + # iteratively construct custom multiplication rule, + # temporarily treating the constant terms as index -1 + operation_rule = [] + for multiset in all_multisets: + rule_indices = [] + + if multiset in ap1.monomial_labels: + idx = ap1.monomial_labels.index(multiset) + rule_indices.append([idx, -1]) + + if multiset in ap2.monomial_labels: + idx = ap2.monomial_labels.index(multiset) + rule_indices.append([-1, idx]) + + if len(multiset) > 1: + for I, J in zip(*_submultisets_and_complements(multiset)): + if I in ap1.monomial_labels and J in ap2.monomial_labels: + rule_indices.append( + [ap1.monomial_labels.index(I), ap2.monomial_labels.index(J)] + ) + + # if non-empty, + if rule_indices: + operation_rule.append((np.ones(len(rule_indices)), np.array(rule_indices))) + + lmats = None + if ap1.constant_term is not None: + lmats = np.expand_dims(ap1.constant_term, 0) + else: + lmats = np.expand_dims(np.zeros_like(Array(ap1.array_coefficients[0])), 0) + + if ap1.array_coefficients is not None: + lmats = np.append(lmats, ap1.array_coefficients, axis=0) + + rmats = None + if ap2.constant_term is not None: + rmats = np.expand_dims(ap2.constant_term, 0) + else: + rmats = np.expand_dims(np.zeros_like(Array(ap2.array_coefficients[0])), 0) + + if ap2.array_coefficients is not None: + rmats = np.append(rmats, ap2.array_coefficients, axis=0) + + custom_binary_op = _CustomBinaryOp( + operation_rule=operation_rule, + binary_op=binary_op, + index_offset=1, + ) + new_array_coefficients = custom_binary_op(lmats, rmats) + + return ArrayPolynomial( + array_coefficients=new_array_coefficients, + monomial_labels=all_multisets, + constant_term=new_constant_term, + ) + + +def _array_polynomial_addition( + ap1: ArrayPolynomial, + ap2: ArrayPolynomial, + monomial_filter: Optional[Callable] = None, +) -> ArrayPolynomial: + """Add two ArrayPolynomials.""" + + for a, b in zip(ap1.shape[::-1], ap2.shape[::-1]): + if not (a == 1 or b == 1 or a == b): + raise QiskitError( + "ArrayPolynomial addition requires shapes be broadcastable to eachother." + ) + + if monomial_filter is None: + monomial_filter = lambda x: True + + # construct constant term + new_constant_term = None + + # if constant term is to be included, determine what it is + if monomial_filter(Multiset({})): + if ap1.constant_term is not None and ap2.constant_term is not None: + new_constant_term = ap1.constant_term + ap2.constant_term + elif ap1.constant_term is not None: + new_constant_term = ap1.constant_term + elif ap2.constant_term is not None: + new_constant_term = ap2.constant_term + + # exit early if both polynomials are constant + if ap1.array_coefficients is None and ap2.array_coefficients is None: + return ArrayPolynomial(constant_term=new_constant_term) + + # construct list of admissable multisets and sort into canonical order + new_multisets = [] + for multiset in ap1.monomial_labels + ap2.monomial_labels: + if monomial_filter(multiset) and multiset not in new_multisets: + new_multisets.append(multiset) + new_multisets = _sorted_multisets(new_multisets) + + # construct index order mapping for each polynomial + idx1 = [] + idx2 = [] + for multiset in new_multisets: + if multiset in ap1.monomial_labels: + idx1.append(ap1.monomial_labels.index(multiset)) + else: + idx1.append(-1) + + if multiset in ap2.monomial_labels: + idx2.append(ap2.monomial_labels.index(multiset)) + else: + idx2.append(-1) + + # if either is empty, pad with single -1, then convert to array + idx1 = idx1 or [-1] + idx2 = idx2 or [-1] + idx1 = np.array(idx1) + idx2 = np.array(idx2) + + # append zero to the coefficient arrays + array_coefficients1 = np.zeros((1,) + ap1.shape, dtype=complex) + array_coefficients2 = np.zeros((1,) + ap1.shape, dtype=complex) + if ap1.array_coefficients is not None: + array_coefficients1 = np.append(ap1.array_coefficients, array_coefficients1, axis=0) + if ap2.array_coefficients is not None: + array_coefficients2 = np.append(ap2.array_coefficients, array_coefficients2, axis=0) + + new_coefficients = array_coefficients1[idx1] + array_coefficients2[idx2] + + return ArrayPolynomial( + array_coefficients=new_coefficients, + monomial_labels=new_multisets, + constant_term=new_constant_term, + ) +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/perturbation/perturbation_data.html b/stable/0.4/_modules/qiskit_dynamics/perturbation/perturbation_data.html new file mode 100644 index 000000000..7e51422f0 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/perturbation/perturbation_data.html @@ -0,0 +1,415 @@ + + + + + + + + qiskit_dynamics.perturbation.perturbation_data - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.perturbation.perturbation_data

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2022.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+r"""
+Class for storing results of perturbation theory computations.
+"""
+
+from typing import List, Mapping, Optional
+from dataclasses import dataclass
+
+from multiset import Multiset
+
+from qiskit import QiskitError
+from qiskit_dynamics.array import Array
+
+
+@dataclass
+class _LabeledData:
+    """Container for arbitrarily "labeled data", i.e. data whose indices are arbitrary python
+    objects. The method ``get_item`` looks up an item according to the label.
+    """
+
+    data: Mapping[int, any]
+    labels: List[any]
+    metadata: Optional[any] = None
+
+    def get_item(self, label: any) -> any:
+        """Look up an item in self.data according to the location of label in self.labels."""
+        label = self._preprocess_label(label)
+
+        if label in self.labels:
+            return self._post_process_item(self.data[self.labels.index(label)])
+
+        raise QiskitError("label is not present in self.labels.")
+
+    def _preprocess_label(self, label: any) -> any:
+        return label
+
+    def _post_process_item(self, item: any) -> any:
+        return item
+
+
+
+[docs] +class PowerSeriesData(_LabeledData): + """Storage container for power series data. Labels are assumed to be ``Multiset`` instances, + and data is assumed to be an ``Array``. + """ + + def _preprocess_label(self, label: Multiset) -> Multiset: + """Cast to a Multiset.""" + return Multiset(label) + + def _post_process_item(self, item: Array) -> Array: + """Wrap in an Array.""" + return Array(item, backend=self.data.backend)
+ + + +
+[docs] +class DysonLikeData(_LabeledData): + """Storage container for DysonLike series data. Labels are assumed to be lists of ints, + and data is assumed to be an ``Array``. + """ + + def _preprocess_label(self, label: list) -> list: + """Cast to a list.""" + return list(label) + + def _post_process_item(self, item: Array) -> Array: + """Wrap in an array.""" + return Array(item, backend=self.data.backend)
+ +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/perturbation/solve_lmde_perturbation.html b/stable/0.4/_modules/qiskit_dynamics/perturbation/solve_lmde_perturbation.html new file mode 100644 index 000000000..08278ab42 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/perturbation/solve_lmde_perturbation.html @@ -0,0 +1,642 @@ + + + + + + + + qiskit_dynamics.perturbation.solve_lmde_perturbation - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.perturbation.solve_lmde_perturbation

+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2022.
+#
+# This code is licensed under the Apache License, Version 2.0. You may obtain a copy of this license
+# in the LICENSE.txt file in the root directory of this source tree or at
+# http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this copyright notice, and modified
+# files need to carry a notice indicating that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+r"""
+Compute perturbation theory terms for an LMDE.
+"""
+
+from typing import List, Optional, Callable
+
+from scipy.integrate._ivp.ivp import OdeResult  # pylint: disable=unused-import
+
+from multiset import Multiset
+
+from qiskit import QiskitError
+
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.perturbation.multiset_utils import _clean_multisets
+from qiskit_dynamics.perturbation.perturbation_utils import (
+    _merge_multiset_expansion_order_labels,
+    _merge_list_expansion_order_labels,
+)
+
+from qiskit_dynamics.perturbation.dyson_magnus import (
+    _solve_lmde_dyson,
+    _solve_lmde_magnus,
+    _solve_lmde_dyson_jax,
+    _solve_lmde_magnus_jax,
+)
+
+
+
+[docs] +def solve_lmde_perturbation( + perturbations: List[Callable], + t_span: Array, + expansion_method: str, + expansion_order: Optional[int] = None, + expansion_labels: Optional[List[Multiset]] = None, + perturbation_labels: Optional[List[Multiset]] = None, + generator: Optional[Callable] = None, + y0: Optional[Array] = None, + dyson_in_frame: Optional[bool] = True, + integration_method: Optional[str] = "DOP853", + t_eval: Optional[Array] = None, + **kwargs, +) -> OdeResult: + r"""Compute time-dependent perturbation theory terms for an LMDE. + + This function computes multi-variable Dyson or Magnus expansion terms via the algorithm in + :footcite:`puzzuoli_sensitivity_2022`, or Dyson-like terms via the algorithm in + :footcite:`haas_engineering_2019`. See the + :ref:`review on time-dependent perturbation theory <perturbation review>` + to understand the details and notation used in this documentation. + + Which expansion is used is specified by the ``expansion_method`` argument, which impacts the + interpretation of several of the function arguments (described below). Regardless of + ``expansion_method``, the main computation is performed by solving a differential equation, + utilizing :func:`.solve_ode`, and as such several of the function arguments are direct inputs + into this function: + + - ``integration_method`` is the ODE method used (passed as ``method`` to + :func:`.solve_ode`), ``t_span`` is the integration interval, and ``t_eval`` is an optional + set of points to evaluate the perturbation terms at. + - ``kwargs`` are passed directly to :func:`.solve_ode`, enabling passing through of + tolerance or step size arguments. + + Other arguments which are treated the same regardless off ``expansion_method`` are: + + - ``generator`` is the unperturbed generator, and the computation is performed in the + toggling frame of this generator. + + If ``expansion_method in ['dyson', 'magnus']``, this function computes either multivariable + Dyson series or Magnus expansion terms. That is, given a (finitely truncated) power series for + the generator: + + .. math:: + + G(t, c_0, \dots, c_{r-1}) = G_\emptyset(t) + + \sum_{k=1}^\infty \sum_{I \in \mathcal{I}_k(r)} c_I G_I(t), + + this function computes, in the toggling frame of :math:`G_\emptyset(t)` given by ``generator``, + either a collection of multivariable Dyson terms :math:`\mathcal{D}_I(t)` or multivariable + Magnus terms :math:`\mathcal{O}_I(t)`, whose definitions are given in the + :ref:`perturbation theory review <perturbation review>`. In this case, the arguments to the + function are interpreted as follows: + + - ``perturbations`` and ``perturbation_labels`` specify the truncated generator power + series. ``perturbations`` provides a list of python callable functions for the non-zero + :math:`G_I(t)`, and the ``perturbation_labels`` is a list of the corresponding multiset + labels :math:`I`, in the form of ``Multiset`` instances. If not specified, the labels are + assumed to be ``[Multiset({0: 1}), ..., Multiset({len(perturbations) - 1: 1})]``. + - ``expansion_order`` and ``expansion_labels`` specify which terms in the chosen expansion + are to be computed. ``expansion_order`` specifies that all expansion terms up to a given + order are to be computed, and ``expansion_labels`` specifies individual terms to be + computed, specified as ``Multiset`` instances. At least one of ``expansion_order`` and + ``expansion_labels`` must be specified. If both are specified, then all terms up to + ``expansion_order`` will be computed, along with any additional specific terms given by + ``expansion_labels``. + + Note that in the above, this function requires that the Multisets consist of non-negative + integers. Arguments requiring lists of ``Multiset`` instances also accept lists of any valid + format acceptable to the ``Multiset`` constructor (modulo the non-negative integer constraint). + + If ``expansion_method == 'dyson_like'``, the setup is different. In this case, for a list of + matrix-valued functions :math:`G_0(t), \dots, G_{r-1}(t)`, this function computes integrals of + the form + + .. math:: + \int_{t_0}^{t_F} dt_1 \int_{t_0}^{t_1} dt_2 \dots \int_{t_0}^{t_{k-1}}dt_k + \tilde{G}_{i_1}(t_1) \dots \tilde{G}_{i_k}(t_k), + + for lists of integers :math:`[i_1, \dots, i_k]`, and similar to the other cases, + :math:`\tilde{G}_j(t) = V(t)^\dagger G_j(t)V(t)`, i.e. the computation is performed in the + toggling frame specified by ``generator``. + + - ``perturbations`` gives the list of matrix functions as callables + :math:`G_0(t), \dots, G_{r-1}(t)`. + - ``perturbation_labels`` is not used in this mode. + - ``expansion_order`` specifies that all possible integrals of the above form should be + computed up to a given order (i.e. integrals up to a given order with all possible + orderings of the :math:`G_0(t), \dots, G_{r-1}(t)`). + - ``expansion_labels`` allows for specification of specific terms to be computed. In this + case, a term is specified by a list of ``int``\s, where the length of the list is the + order of the integral, and the :math:`G_0(t), \dots, G_{r-1}(t)` appear in the integral in + the order given by the list. + + Finally, additional optional arguments which can be used in the ``'dyson'`` and ``'dyson_like'`` + cases are: + + - ``dyson_in_frame`` controls which frame the results are returned in. The default is + ``True``, in which case the results are returned as described above. If ``False``, the + returned results include a pre-factor of :math:`V(t)`, the solution of the unperturbed + generator, e.g. :math:`V(t)\mathcal{D}_I(t)`. If ``expansion_method=='magnus'``, this + argument has no effect on the computation. + - ``y0`` is the initial state for the LMDE given by the unperturbed generator. The effect of + this argument on the output is to multiply all outputs by ``y0`` on the right. If ``y0`` + is supplied, the argument ``dyson_in_frame`` must be ``False``, as the operator + :math:`V(t)` is never explicitly computed, and therefore it cannot be removed from the + results. If ``y0`` is 1d, it will first be transformed into a 2d column vector to ensure + consistent usage of matrix multiplication. + + Regardless of the value of ``expansion_method``, results are returned in an ``OdeResult`` + instance in the same manner as :func:`.solve_ode`. The result object stores the solution of the + LMDE for ``generator`` and ``y0`` as if these were passed directly to :func:`.solve_ode` before, + as well as the computed perturbation theory terms in the additional attribute + ``perturbation_data``. If ``expansion_method in ['dyson', 'magnus']``, the ``perturbation_data`` + attribute stores a :class:`.PowerSeriesData` instance, and if + ``expansion_method == 'dyson_like'``, it stores a :class:`.DysonLikeData` instance. In either + case, these are data container classes with the following attributes: + + - ``metadata``: Containing expansion information. + - ``labels``: Index labels for all computed perturbation terms. In the case of the Dyson or + Magnus expansion, the labels are ``Multiset`` instances, and in the `'dyson_like'` case, + they are lists of ``int``\s. + - ``data``: A 4d array storing all computed terms. The first axis indexes the expansion + terms in the same ordering as ``labels``, and the second axis indexes the perturbation + terms evaluated at the times in ``results.t`` in the same manner as ``results.y``. + + Additionally, both the :meth:`.PowerSeriesData.get_item` and :meth:`.DysonLikeData.get_item` + methods can be used to retrieve the results for a given perturbation term according to its + label. E.g. the results for the term with label ``[0, 1]`` is retrievable via + ``results.perturbation_data.get_term([0, 1])``. + + Args: + perturbations: List of matrix-valued callables. + t_span: Integration bounds. + expansion_method: Either ``'dyson'``, ``'magnus'``, or ``'dyson_like'``. + expansion_order: Order of perturbation terms to compute up to. Specifying this argument + results in computation of all terms up to the given order. Can be used in conjunction + with ``expansion_labels``. + expansion_labels: Specific perturbation terms to compute. If both ``expansion_order`` + and ``expansion_labels`` are specified, then all terms up to ``expansion_order`` are + computed, along with the additional terms specified in ``expansion_labels``. + perturbation_labels: Optional description of power series terms specified by + ``perturbations``. To only be used with ``'dyson'`` and ``'magnus'`` methods. + generator: Optional frame generator. Defaults to ``0``. + y0: Optional initial state for frame generator LMDE. Defaults to the identity matrix. + dyson_in_frame: For ``expansion_method`` ``'dyson'`` or ``'dyson_like'``, whether or not + to remove the frame transformation pre-factor from the Dyson terms. + integration_method: Integration method to use. + t_eval: Points at which to evaluate the system. + **kwargs: Additional arguments to pass to ode integration method used to compute terms. + + Returns: + OdeResult: Results object containing standard ODE results for LMDE given by ``generator`` + and ``y0``, with additional ``perturbation_data`` attribute containing the requested + perturbation theory terms. + + Raises: + QiskitError: If problem with inputs, either ``expansion_method`` is unsupported, + or both of ``expansion_order`` and ``expansion_labels`` unspecified. + + .. footbibliography:: + """ + + # validation checks + if y0 is not None: + if "magnus" in expansion_method: + raise QiskitError("""Argument y0 cannot be used for expansion_method=='magnus'.""") + + if dyson_in_frame: + raise QiskitError( + """If expansion_method in ['dyson', 'dyson_like'] + and y0 passed, dyson_in_frame must be False.""" + ) + + # if 1d in a dyson case, turn into a column vector + if y0.ndim == 1: + y0 = Array([y0]).transpose() + + if perturbation_labels is not None and expansion_method == "dyson_like": + raise QiskitError( + """perturbation_labels argument not usable with + expansion_method='dyson_like'.""" + ) + + # clean and validate perturbation_labels, and setup expansion terms to compute + if expansion_method in ["dyson", "magnus"]: + if perturbation_labels is None: + perturbation_labels = [Multiset({idx: 1}) for idx in range(len(perturbations))] + else: + # validate perturbation_labels + perturbations_len = len(perturbation_labels) + perturbation_labels = [Multiset(x) for x in perturbation_labels] + cleaned_perturbation_labels = _clean_multisets(perturbation_labels) + if len(cleaned_perturbation_labels) != perturbations_len: + raise QiskitError("perturbation_labels argument contains duplicates as multisets.") + + expansion_labels = _merge_multiset_expansion_order_labels( + perturbation_labels=perturbation_labels, + expansion_order=expansion_order, + expansion_labels=expansion_labels, + ) + elif expansion_method in ["dyson_like"]: + expansion_labels = _merge_list_expansion_order_labels( + perturbation_num=len(perturbations), + expansion_order=expansion_order, + expansion_labels=expansion_labels, + ) + + if expansion_method in ["dyson", "dyson_like"]: + dyson_like = expansion_method == "dyson_like" + if not Array.default_backend() == "jax": + return _solve_lmde_dyson( + perturbations=perturbations, + t_span=t_span, + dyson_terms=expansion_labels, + perturbation_labels=perturbation_labels, + generator=generator, + y0=y0, + dyson_in_frame=dyson_in_frame, + dyson_like=dyson_like, + integration_method=integration_method, + t_eval=t_eval, + **kwargs, + ) + else: + return _solve_lmde_dyson_jax( + perturbations=perturbations, + t_span=t_span, + dyson_terms=expansion_labels, + perturbation_labels=perturbation_labels, + generator=generator, + y0=y0, + dyson_in_frame=dyson_in_frame, + dyson_like=dyson_like, + integration_method=integration_method, + t_eval=t_eval, + **kwargs, + ) + elif expansion_method == "magnus": + if not Array.default_backend() == "jax": + return _solve_lmde_magnus( + perturbations=perturbations, + t_span=t_span, + magnus_terms=expansion_labels, + perturbation_labels=perturbation_labels, + generator=generator, + y0=y0, + integration_method=integration_method, + t_eval=t_eval, + **kwargs, + ) + else: + return _solve_lmde_magnus_jax( + perturbations=perturbations, + t_span=t_span, + magnus_terms=expansion_labels, + perturbation_labels=perturbation_labels, + generator=generator, + y0=y0, + integration_method=integration_method, + t_eval=t_eval, + **kwargs, + ) + + # raise error if none apply + raise QiskitError(f"expansion_method {expansion_method} not supported.")
+ +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/pulse/pulse_to_signals.html b/stable/0.4/_modules/qiskit_dynamics/pulse/pulse_to_signals.html new file mode 100644 index 000000000..eddad47b2 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/pulse/pulse_to_signals.html @@ -0,0 +1,736 @@ + + + + + + + + qiskit_dynamics.pulse.pulse_to_signals - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.pulse.pulse_to_signals

+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""
+Pulse schedule to Signals converter.
+"""
+
+from typing import Callable, Dict, List, Optional
+import functools
+from warnings import warn
+
+import numpy as np
+import sympy as sym
+
+from qiskit.pulse import (
+    Schedule,
+    Play,
+    ShiftPhase,
+    SetPhase,
+    ShiftFrequency,
+    SetFrequency,
+    Waveform,
+    MeasureChannel,
+    DriveChannel,
+    ControlChannel,
+    AcquireChannel,
+)
+from qiskit.pulse.exceptions import PulseError
+from qiskit.pulse.library import SymbolicPulse
+from qiskit import QiskitError
+
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.signals import DiscreteSignal
+
+try:
+    import jax
+    import jax.numpy as jnp
+except ImportError:
+    pass
+
+
+
+[docs] +class InstructionToSignals: + """Converts pulse instructions to signals to be used in models. + + The :class:`InstructionsToSignals` class converts a pulse schedule to a list of signals that can + be given to a model. This conversion is done by calling the :meth:`get_signals` method on a + schedule. The converter applies to instances of :class:`~qiskit.pulse.Schedule`. Instances of + :class:`~qiskit.pulse.ScheduleBlock` must first be converted to :class:`~qiskit.pulse.Schedule` + using the :func:`~qiskit.pulse.transforms.block_to_schedule` function in Qiskit Pulse. + + The converter can be initialized with the optional arguments ``carriers`` and ``channels``. When + ``channels`` is given, only the signals specified by name in ``channels`` are returned. The + ``carriers`` dictionary specifies the analog carrier frequency of each channel. Here, the keys + are the channel name, e.g. ``d12`` for drive channel number ``12``, and the values are the + corresponding frequency. If a channel is not present in ``carriers`` it is assumed that the + analog carrier frequency is zero. + + See the :meth:`get_signals` method documentation for a detailed description of how pulse + schedules are interpreted and translated into :class:`.DiscreteSignal` objects. + """ + + def __init__( + self, + dt: float, + carriers: Optional[Dict[str, float]] = None, + channels: Optional[List[str]] = None, + ): + """Initialize pulse schedule to signals converter. + + Args: + dt: Length of the samples. This is required by the converter as pulse schedule are + specified in units of dt and typically do not carry the value of dt with them. + carriers: A dict of analog carrier frequencies. The keys are the names of the channels + and the values are the corresponding carrier frequency. + channels: A list of channels that the :meth:`get_signals` method should return. This + argument will cause :meth:`get_signals` to return the signals in the same order as + the channels. Channels present in the schedule but absent from channels will not be + included in the returned object. If None is given (the default) then all channels + present in the pulse schedule are returned. + """ + + self._dt = dt + self._channels = channels + self._carriers = carriers or {} + +
+[docs] + def get_signals(self, schedule: Schedule) -> List[DiscreteSignal]: + r"""Convert a schedule to a corresponding list of DiscreteSignal instances. + + Which channels are converted, and the order they are returned, is controlled by the + ``channels`` argument at instantiation. The ``carriers`` instantiation argument sets the + analog carrier frequency for each channel, which is fixed for the full duration. For a given + channel, the :math:`k^{th}` envelope sample for the corresponding :class:`.DiscreteSignal` + is determined according to the following formula: + + .. math:: + f(k) \exp(i(2\pi \Delta\nu(k) k dt + \phi(k) + 2 \pi \phi_a(k))), + + where: + + * :math:`f(k)` is the waveform value at the :math:`k^{th}` time step as specified by + ``Play`` instructions. + * :math:`\Delta\nu(k)` is the frequency deviation at time step :math:`k` from the analog + carrier as the result of ``SetFrequency`` and ``ShiftFrequency`` instructions. As evident + by the formula, carrier frequency deviations as a result of these instructions are handled + digitally, with the analog carrier frequency being fixed for the entirety of the schedule. + * :math:`dt` is the sample rate as specified by the ``dt`` instantiation argument. + * :math:`\phi(k)` is the channel phase at time step :math:`k`, as determined by + ``ShiftPhase`` and ``SetPhase`` instructions. + * :math:`\phi_a(k)` is the phase correction term at time step :math:`k`, impacted by + ``SetFrequency`` and ``ShiftFrequency`` instructions, described below. + + In detail, the sample array for the output signal for each channel is generated by iterating + over each instruction in the schedule in temporal order. New samples are appended with every + ``Play`` instruction on the given channel, using the waveform values and the current value + of the tracked parameters :math:`\Delta\nu`, :math:`\phi`, and :math:`\phi_a`, which are + initialized to :math:`0`. Explicitly, each instruction is interpreted as follows: + + * ``Play`` instructions add new samples to the sample array, according to the above formula, + using the waveform specified in the instruction and the current values of + :math:`\Delta\nu`, :math:`\phi`, and :math:`\phi_a`. + * ``ShiftPhase``, with a phase value :math:`\psi`, updates :math:`\phi \mapsto \phi + \psi`. + * ``SetPhase``, with a phase value :math:`\psi`, updates :math:`\phi \mapsto \psi`. + * ``ShiftFrequency``, with a frequency value :math:`\mu` at time-step :math:`k`, updates + :math:`\phi_a \mapsto \phi_a - \mu k dt` and :math:`\Delta\nu \mapsto \Delta\nu + \mu`. + The simultaneous shifting of both :math:`\Delta\nu` and :math:`\phi_a` ensures that the + carrier wave, as a combination of the analog and digital components, is continuous across + ``ShiftFrequency`` instructions (up to the sampling rate :math:`dt`). + * ``SetFrequency``, with a frequency value :math:`\mu` at time-step :math:`k`, updates + :math:`\phi_a \mapsto \phi_a - (\mu - (\Delta\nu + \nu)) k dt` and + :math:`\Delta\nu \mapsto \mu - \nu`, where :math:`\nu` is the analog carrier frequency. + Similarly to ``ShiftFrequency``, the shift rule for :math:`\phi_a` is defined to maintain + carrier wave continuity. + + If, at any sample point :math:`k`, :math:`\Delta\nu(k)` is larger than the Nyquist sampling + rate given by ``dt``, a warning will be raised. + + Args: + schedule: The schedule to represent in terms of signals. Instances of + :class:`~qiskit.pulse.ScheduleBlock` must first be converted to + :class:`~qiskit.pulse.Schedule` using the + :func:`~qiskit.pulse.transforms.block_to_schedule` function in Qiskit Pulse. + + Returns: + A list of :class:`.DiscreteSignal` instances. + """ + + signals, phases, frequency_shifts, phase_accumulations = {}, {}, {}, {} + + if self._channels is not None: + schedule = schedule.filter(channels=[self._get_channel(ch) for ch in self._channels]) + + for chan in schedule.channels: + phases[chan.name] = 0.0 + frequency_shifts[chan.name] = 0.0 + phase_accumulations[chan.name] = 0.0 + + carrier_freq = self._carriers.get(chan.name, 0.0) + + signals[chan.name] = DiscreteSignal( + samples=[], + dt=self._dt, + name=chan.name, + carrier_freq=carrier_freq, + ) + + for start_sample, inst in schedule.instructions: + # get channel name if instruction has it + chan = inst.channel.name if hasattr(inst, "channel") else None + + if isinstance(inst, Play): + # get the instruction samples + inst_samples = None + if isinstance(inst.pulse, Waveform): + inst_samples = inst.pulse.samples + else: + inst_samples = get_samples(inst.pulse) + + # build sample array to append to signal + times = self._dt * (start_sample + np.arange(len(inst_samples))) + samples = inst_samples * np.exp( + Array( + 2.0j * np.pi * frequency_shifts[chan] * times + + 1.0j * phases[chan] + + 2.0j * np.pi * phase_accumulations[chan] + ) + ) + signals[chan].add_samples(start_sample, samples) + + if isinstance(inst, ShiftPhase): + phases[chan] += inst.phase + + if isinstance(inst, SetPhase): + phases[chan] = inst.phase + + if isinstance(inst, ShiftFrequency): + frequency_shifts[chan] = frequency_shifts[chan] + Array(inst.frequency) + phase_accumulations[chan] = ( + phase_accumulations[chan] - inst.frequency * start_sample * self._dt + ) + _nyquist_warn(frequency_shifts[chan], self._dt, chan) + + if isinstance(inst, SetFrequency): + phase_accumulations[chan] = phase_accumulations[chan] - ( + (inst.frequency - (frequency_shifts[chan] + signals[chan].carrier_freq)) + * start_sample + * self._dt + ) + frequency_shifts[chan] = inst.frequency - signals[chan].carrier_freq + _nyquist_warn(frequency_shifts[chan], self._dt, chan) + + # ensure all signals have the same number of samples + max_duration = 0 + for sig in signals.values(): + max_duration = max(max_duration, sig.duration) + + for sig in signals.values(): + if sig.duration < max_duration: + sig.add_samples( + start_sample=sig.duration, + samples=np.zeros(max_duration - sig.duration, dtype=complex), + ) + + # filter the channels + if self._channels is None: + return list(signals.values()) + + return_signals = [] + for chan_name in self._channels: + signal = signals.get( + chan_name, DiscreteSignal(samples=[], dt=self._dt, name=chan_name, carrier_freq=0.0) + ) + + return_signals.append(signal) + return return_signals
+ + +
+[docs] + @staticmethod + def get_awg_signals( + signals: List[DiscreteSignal], if_modulation: float + ) -> List[DiscreteSignal]: + r""" + Create signals that correspond to the output ports of an Arbitrary Waveform Generator + to be used with IQ mixers. For each signal in the list the number of signals is double + to create the I and Q components. The I and Q signals represent the real and imaginary + parts, respectively, of + + .. math:: + \Omega(t) e^{i \omega_{if} t} + + where :math:`\Omega` is the complex-valued pulse envelope and :math:`\omega_{if}` is the + intermediate frequency. + + Args: + signals: A list of signals for which to create I and Q. + if_modulation: The intermediate frequency with which the AWG modulates the pulse + envelopes. + + Returns: + iq signals: A list of signals which is twice as long as the input list of signals. + For each input signal get_awg_signals returns two + """ + new_signals = [] + + for sig in signals: + new_freq = sig.carrier_freq + if_modulation + + samples_i = sig.samples + samples_q = np.imag(samples_i) - 1.0j * np.real(samples_i) + + sig_i = DiscreteSignal( + sig.dt, + samples_i, + sig.start_time, + new_freq, + sig.phase, + sig.name + "_i", + ) + sig_q = DiscreteSignal( + sig.dt, + samples_q, + sig.start_time, + new_freq, + sig.phase, + sig.name + "_q", + ) + + new_signals += [sig_i, sig_q] + + return new_signals
+ + + def _get_channel(self, channel_name: str): + """Return the channel corresponding to the given name.""" + + try: + prefix = channel_name[0] + index = int(channel_name[1:]) + + if prefix == "d": + return DriveChannel(index) + + if prefix == "m": + return MeasureChannel(index) + + if prefix == "u": + return ControlChannel(index) + + if prefix == "a": + return AcquireChannel(index) + + raise QiskitError( + f"Unsupported channel name {channel_name} in {self.__class__.__name__}" + ) + + except (KeyError, IndexError, ValueError) as error: + raise QiskitError( + f"Invalid channel name {channel_name} given to {self.__class__.__name__}." + ) from error
+ + + +def get_samples(pulse: SymbolicPulse) -> np.ndarray: + """Return samples filled according to the formula that the pulse + represents and the parameter values it contains. + + Args: + pulse: SymbolicPulse class. + Returns: + Samples of the pulse. + Raises: + PulseError: When parameters are not assigned. + PulseError: When expression for pulse envelope is not assigned. + PulseError: When a free symbol value is not defined in the pulse instance parameters. + """ + envelope = pulse.envelope + pulse_params = pulse.parameters + if pulse.is_parameterized(): + raise PulseError("Unassigned parameter exists. All parameters must be assigned.") + + if envelope is None: + raise PulseError("Pulse envelope expression is not assigned.") + + args = [] + for symbol in sorted(envelope.free_symbols, key=lambda s: s.name): + if symbol.name == "t": + times = Array(np.arange(0, pulse_params["duration"]) + 1 / 2) + args.insert(0, times.data) + continue + try: + args.append(pulse_params[symbol.name]) + except KeyError as ex: + raise PulseError( + f"Pulse parameter '{symbol.name}' is not defined for this instance. " + "Please check your waveform expression is correct." + ) from ex + return _lru_cache_expr(envelope, Array.default_backend())(*args) + + +@functools.lru_cache(maxsize=None) +def _lru_cache_expr(expr: sym.Expr, backend) -> Callable: + """A helper function to get lambdified expression. + + Args: + expr: Symbolic expression to evaluate. + backend: Array backend. + Returns: + lambdified expression. + """ + params = [] + for param in sorted(expr.free_symbols, key=lambda s: s.name): + if param.name == "t": + params.insert(0, param) + continue + params.append(param) + return sym.lambdify(params, expr, modules=backend) + + +def _nyquist_warn(frequency_shift: Array, dt: float, channel: str): + """Raise a warning if the frequency shift is above the Nyquist frequency given by ``dt``.""" + + if ( + Array(frequency_shift).backend != "jax" or not isinstance(jnp.array(0), jax.core.Tracer) + ) and np.abs(frequency_shift) > 0.5 / dt: + warn( + "Due to SetFrequency and ShiftFrequency instructions, the digital carrier frequency " + f"of channel {channel} is larger than the Nyquist frequency of the envelope sample " + "size dt. As shifts of the frequency from the analog frequency are handled digitally, " + "this will result in aliasing effects." + ) +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/signals/signals.html b/stable/0.4/_modules/qiskit_dynamics/signals/signals.html new file mode 100644 index 000000000..baa9a3197 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/signals/signals.html @@ -0,0 +1,1550 @@ + + + + + + + + qiskit_dynamics.signals.signals - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.signals.signals

+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2021.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name, unidiomatic-typecheck, super-init-not-called
+
+"""
+Module for representation of model coefficients.
+"""
+
+from typing import List, Callable, Union, Optional, Tuple
+import itertools
+import operator
+
+import numpy as np
+from matplotlib import pyplot as plt
+
+try:
+    import jax.numpy as jnp
+except ImportError:
+    pass
+
+from qiskit import QiskitError
+from qiskit_dynamics.array import Array
+
+
+
+[docs] +class Signal: + r"""General signal class. + + Represents a function of the form: + + .. math:: + Re[f(t)e^{i (2 \pi \nu t + \phi)}] + = Re[f(t)]\cos(2 \pi \nu t + \phi) - Im[f(t)]\sin(2 \pi \nu t + \phi), + + where + + - :math:`f(t)` is the envelope function. + - :math:`\nu` is the carrier frequency. + - :math:`\phi` is the phase. + + The envelope function can be specified either as a constant numeric value + (indicating a constant function), or as a complex-valued callable, + and the frequency and phase must be real. + + + .. note:: + + :class:`~qiskit_dynamics.signals.Signal` assumes the envelope ``f`` is + *array vectorized* in the sense that ``f`` can operate on arrays of arbitrary shape + and satisfy ``f(x)[idx] == f(x[idx])`` for a multidimensional index ``idx``. This + can be ensured either by writing ``f`` to be vectorized, or by using the ``vectorize`` + function in ``numpy`` or ``jax.numpy``. + + For example, for an unvectorized envelope function ``f``: + + .. code-block:: python + + import numpy as np + vectorized_f = np.vectorize(f) + signal = Signal(envelope=vectorized_f, carrier_freq=2.) + """ + + def __init__( + self, + envelope: Union[Callable, complex, float, int, Array], + carrier_freq: Union[float, List, Array] = 0.0, + phase: Union[float, List, Array] = 0.0, + name: Optional[str] = None, + ): + """ + Initializes a signal given by an envelope and a carrier. + + Args: + envelope: Envelope function of the signal, must be vectorized. + carrier_freq: Frequency of the carrier. Subclasses such as SignalSums + represent the carriers of each signal in an array. + phase: The phase of the carrier. Subclasses such as SignalSums + represent the phase of each signal in an array. + name: Name of the signal. + """ + self._name = name + self._is_constant = False + + if isinstance(envelope, (complex, float, int)): + envelope = Array(complex(envelope)) + + if isinstance(envelope, Array): + # if envelope is constant and the carrier is zero, this is a constant signal + try: + # try block is for catching JAX tracer errors + if carrier_freq == 0.0: + self._is_constant = True + except Exception: # pylint: disable=broad-except + pass + + if envelope.backend == "jax": + self._envelope = lambda t: envelope * jnp.ones_like(Array(t).data) + else: + self._envelope = lambda t: envelope * np.ones_like(t) + elif callable(envelope): + if Array.default_backend() == "jax": + self._envelope = lambda t: Array(envelope(t)) + else: + self._envelope = envelope + + # set carrier and phase + self.carrier_freq = carrier_freq + self.phase = phase + + @property + def name(self) -> str: + """Return the name of the signal.""" + return self._name + + @property + def is_constant(self) -> bool: + """Whether or not the signal is constant.""" + return self._is_constant + + @property + def carrier_freq(self) -> Array: + """The carrier frequency of the signal.""" + return self._carrier_freq + + @carrier_freq.setter + def carrier_freq(self, carrier_freq: Union[float, list, Array]): + """Carrier frequency setter. List handling is to support subclasses storing a + list of frequencies.""" + if type(carrier_freq) == list: + carrier_freq = [Array(entry).data for entry in carrier_freq] + self._carrier_freq = Array(carrier_freq) + self._carrier_arg = 1j * 2 * np.pi * self._carrier_freq + + @property + def phase(self) -> Array: + """The phase of the signal.""" + return self._phase + + @phase.setter + def phase(self, phase: Union[float, list, Array]): + """Phase setter. List handling is to support subclasses storing a + list of phases.""" + if type(phase) == list: + phase = [Array(entry).data for entry in phase] + self._phase = Array(phase) + self._phase_arg = 1j * self._phase + +
+[docs] + def envelope(self, t: Union[float, np.array, Array]) -> Union[complex, np.array, Array]: + """Vectorized evaluation of the envelope at time t.""" + return self._envelope(t)
+ + +
+[docs] + def complex_value(self, t: Union[float, np.array, Array]) -> Union[complex, np.array, Array]: + """Vectorized evaluation of the complex value at time t.""" + arg = self._carrier_arg * t + self._phase_arg + return self.envelope(t) * np.exp(arg)
+ + + def __call__(self, t: Union[float, np.array, Array]) -> Union[complex, np.array, Array]: + """Vectorized evaluation of the signal at time(s) t.""" + return np.real(self.complex_value(t)) + + def __str__(self) -> str: + """Return string representation.""" + if self.name is not None: + return str(self.name) + + if self.is_constant: + return f"Constant({str(self(0.0))})" + + return f"Signal(carrier_freq={str(self.carrier_freq)}, phase={str(self.phase)})" + + def __add__(self, other: "Signal") -> "SignalSum": + return signal_add(self, other) + + def __radd__(self, other: "Signal") -> "SignalSum": + return self.__add__(other) + + def __mul__(self, other: "Signal") -> "SignalSum": + return signal_multiply(self, other) + + def __rmul__(self, other: "Signal") -> "SignalSum": + return self.__mul__(other) + + def __neg__(self) -> "SignalSum": + return -1 * self + + def __sub__(self, other: "Signal") -> "SignalSum": + return self + (-other) + + def __rsub__(self, other: "Signal") -> "SignalSum": + return other + (-self) + +
+[docs] + def conjugate(self): + """Return a new signal whose complex value is the complex conjugate of this one.""" + + def conj_env(t): + return np.conjugate(self.envelope(t)) + + return Signal(conj_env, -self.carrier_freq, -self.phase)
+ + +
+[docs] + def draw( + self, + t0: float, + tf: float, + n: int, + function: Optional[str] = "signal", + axis: Optional[plt.axis] = None, + title: Optional[str] = None, + ): + """Plot the signal over an interval. + + The ``function`` arg specifies which function to + plot: + + - ``function == 'signal'`` plots the full signal. + - ``function == 'envelope'`` plots the complex envelope. + - ``function == 'complex_value'`` plots the ``complex_value``. + + Args: + t0: Initial time. + tf: Final time. + n: Number of points to sample in interval. + function: Which function to plot. + axis: The axis to use for plotting. + title: Title of plot. + """ + + if axis is None: + plotter = plt + plotter.title(title) + else: + plotter = axis + plotter.set_title(title) + + t_vals = np.linspace(t0, tf, n) + + y_vals = None + data_type = "real" + if function == "signal": + y_vals = self(t_vals) + title = title or "Value of " + str(self) + elif function == "envelope": + y_vals = self.envelope(t_vals) + data_type = "complex" + title = title or "Envelope of " + str(self) + elif function == "complex_value": + y_vals = self.complex_value(t_vals) + data_type = "complex" + title = title or "Complex value of " + str(self) + + legend = False + if data_type == "complex": + plotter.plot(t_vals, np.real(y_vals), label="Real") + plotter.plot(t_vals, np.imag(y_vals), label="Imag") + legend = True + else: + plotter.plot(t_vals, y_vals) + + if legend: + plotter.legend()
+
+ + + +
+[docs] +class DiscreteSignal(Signal): + r"""Piecewise constant signal implemented as an array of samples. + + The envelope is specified by an array of samples ``s = [s_0, ..., s_k]``, sample width ``dt``, + and a start time ``t_0``, with the envelope being evaluated as + :math:`f(t) =` ``s[floor((t - t0)/dt)]`` if ``t`` is in the interval with endpoints + ``start_time`` and ``start_time + dt * len(samples)``, and ``0.0`` otherwise. + By default a :class:`~qiskit_dynamics.signals.DiscreteSignal` is defined to start at + :math:`t=0` but a custom start time can be set via the ``start_time`` kwarg. + """ + + def __init__( + self, + dt: float, + samples: Union[Array, List], + start_time: float = 0.0, + carrier_freq: Union[float, List, Array] = 0.0, + phase: Union[float, List, Array] = 0.0, + name: str = None, + ): + """Initialize a piecewise constant signal. + + Args: + dt: The duration of each sample. + samples: The array of samples. + start_time: The time at which the signal starts. + carrier_freq: Frequency of the carrier. Subclasses such as SignalSums + represent the carriers of each signal in an array. + phase: The phase of the carrier. Subclasses such as SignalSums + represent the phase of each signal in an array. + name: name of the signal. + """ + self._dt = dt + + samples = Array(samples) + + if len(samples) == 0: + zero_pad = np.array([0]) + else: + zero_pad = np.expand_dims(np.zeros_like(Array(samples[0])), 0) + self._padded_samples = np.append(samples, zero_pad, axis=0) + + self._start_time = start_time + + # define internal envelope function + if samples.backend == "jax": + + def envelope(t): + t = Array(t).data + idx = jnp.clip( + jnp.array((t - self._start_time) // self._dt, dtype=int), + -1, + len(self.samples), + ) + return self._padded_samples[idx] + + else: + + def envelope(t): + t = Array(t).data + idx = np.clip( + np.array((t - self._start_time) // self._dt, dtype=int), + -1, + len(self.samples), + ) + return self._padded_samples[idx] + + Signal.__init__(self, envelope=envelope, carrier_freq=carrier_freq, phase=phase, name=name) + +
+[docs] + @classmethod + def from_Signal( + cls, + signal: Signal, + dt: float, + n_samples: int, + start_time: Optional[float] = 0.0, + sample_carrier: Optional[bool] = False, + ): + r"""Constructs a ``DiscreteSignal`` object by sampling a ``Signal``\. + + The optional argument ``sample_carrier`` controls whether or not to include the carrier + in the sampling. I.e.: + + - If ``sample_carrier == False``\, a ``DiscreteSignal`` is constructed with: + - ``samples`` obtained by sampling ``signal.envelope``\. + - ``carrier_freq = signal.carrier_freq``\. + - ``phase = signal.phase``\. + + - If ``sample_carrier == True``\, a ``DiscreteSignal`` is constructed with: + - ``samples`` obtained by sampling ``signal`` (as a ``callable``\) + - ``carrier_freq = 0``\. + - ``phase = signal.phase``\. + + In either case, samples are obtained from the midpoint of each interval. + + Args: + signal: Signal to sample. + dt: Time increment to use. + n_samples: Number of steps to resample with. + start_time: Start time from which to resample. + sample_carrier: Whether or not to include the carrier in the sampling. + + Returns: + DiscreteSignal: The discretized ``Signal``\. + """ + + times = start_time + (np.arange(n_samples) + 0.5) * dt + freq = signal.carrier_freq + + if sample_carrier: + freq = 0.0 + samples = signal(times) + else: + samples = signal.envelope(times) + + return DiscreteSignal( + dt, + samples, + start_time=start_time, + carrier_freq=freq, + phase=signal.phase, + name=signal.name, + )
+ + + @property + def duration(self) -> int: + """ + Returns: + duration: The duration of the signal in samples. + """ + return len(self.samples) + + @property + def dt(self) -> float: + """ + Returns: + dt: the duration of each sample. + """ + return self._dt + + @property + def samples(self) -> Array: + """ + Returns: + samples: the samples of the piecewise constant signal. + """ + return Array(self._padded_samples[:-1]) + + @property + def start_time(self) -> float: + """ + Returns: + start_time: The time at which the list of samples start. + """ + return self._start_time + +
+[docs] + def conjugate(self): + return self.__class__( + dt=self._dt, + samples=np.conjugate(self.samples), + start_time=self._start_time, + carrier_freq=-self.carrier_freq, + phase=-self.phase, + )
+ + +
+[docs] + def add_samples(self, start_sample: int, samples: List): + """ + Appends samples to the pulse starting at start_sample, filling any gap with zeros. + + Args: + start_sample: Index of the sample at which the new samples should be appended. + samples: List of samples to append. + + Raises: + QiskitError: If start_sample is less than the current length of samples. + """ + samples = Array(samples) + + if len(samples) < 1: + return + + if start_sample < len(self.samples): + raise QiskitError("Samples can only be added afer the last sample.") + + zero_pad = np.expand_dims(np.zeros_like(Array(samples[0])), 0) + + new_samples = self.samples + if len(self.samples) < start_sample: + new_samples = np.append( + new_samples, np.repeat(zero_pad, start_sample - len(self.samples)) + ) + + new_samples = np.append(new_samples, samples) + self._padded_samples = np.append(new_samples, zero_pad, axis=0)
+ + + def __str__(self) -> str: + """Return string representation.""" + if self.name is not None: + return str(self.name) + + return f"""DiscreteSignal(dt={self.dt}, carrier_freq={str(self.carrier_freq)}, + phase={str(self.phase)})"""
+ + + +class SignalCollection: + """Base class for a list-like collection of signals.""" + + def __init__(self, signal_list: List[Signal]): + """Initialize by storing list of signals. + + Args: + signal_list: List of signals. + """ + self._is_constant = False + self._components = signal_list + + @property + def components(self) -> List[Signal]: + """The list of components.""" + return self._components + + def __len__(self): + """Number of components.""" + return len(self.components) + + def __getitem__( + self, idx: Union[int, List, np.array, slice] + ) -> Union[Signal, "SignalCollection"]: + """Get item with NumPy-style subscripting, as if this class were a 1d array.""" + + if type(idx) == np.ndarray and idx.ndim > 0: + idx = list(idx) + + # get a list of the subcomponents + if type(idx) == list: + # handle lists + sublist = operator.itemgetter(*idx)(self.components) + + # output will either be a single signal or a tuple of Signals + # convert to list if tuple + if type(sublist) == tuple: + sublist = list(sublist) + else: + # handle slices or singletons + sublist = operator.itemgetter(idx)(self.components) + + # at this point sublist should either be a single Signal, or a list of Signals + if isinstance(sublist, list): + return self.__class__(sublist) + else: + return sublist + + def __iter__(self): + """Return iterator over component list.""" + return self.components.__iter__() + + def conjugate(self) -> "SignalCollection": + """Return the conjugation of this collection.""" + return self.__class__([sig.conjugate() for sig in self.components]) + + +
+[docs] +class SignalSum(SignalCollection, Signal): + r"""Represents a sum of signals. + + I.e. a sum of ``Signal`` objects: + + .. math:: + s_1(t) + \dots + s_k(t) + + For basic evaluation, this class behaves in the same manner as ``Signal``: + + - ``__call__`` evaluates the sum. + - ``complex_value`` evaluates the sum of the complex values of the individual summands. + + Attributes ``carrier_freq`` and ``phase`` here correspond to an ``Array`` of + frequencies/phases for each term in the sum, and the ``envelope`` method returns an + ``Array`` of the envelopes for each summand. + + Internally, the signals are stored as a list in the ``components`` attribute, which can + be accessed via direct subscripting of the object. + """ + + def __init__(self, *signals, name: Optional[str] = None): + r"""Initialize with a list of Signal objects through ``args``\. + + Args: + signals: ``Signal`` subclass objects. + name: Name of the sum. + + Raises: + QiskitError: If ``signals`` are not subclasses of ``Signal``\. + """ + self._name = name + + components = [] + for sig in signals: + if isinstance(sig, list): + sig = SignalSum(*sig) + + if isinstance(sig, SignalSum): + components += sig.components + elif isinstance(sig, Signal): + components.append(sig) + elif isinstance(sig, (int, float, complex)) or ( + isinstance(sig, Array) and sig.ndim == 0 + ): + components.append(Signal(sig)) + else: + raise QiskitError( + "Components of a SignalSum must be instances of a Signal subclass." + ) + + SignalCollection.__init__(self, components) + + # set up routine for evaluating envelopes if jax + if Array.default_backend() == "jax": + jax_arraylist_eval = array_funclist_evaluate([sig.envelope for sig in self.components]) + + def envelope(t): + return np.moveaxis(jax_arraylist_eval(t), 0, -1) + + else: + + def envelope(t): + return np.moveaxis([sig.envelope(t) for sig in self.components], 0, -1) + + carrier_freqs = [] + for sig in self.components: + carrier_freqs.append(sig.carrier_freq) + + phases = [] + for sig in self.components: + phases.append(sig.phase) + + Signal.__init__( + self, envelope=envelope, carrier_freq=carrier_freqs, phase=phases, name=name + ) + +
+[docs] + def complex_value(self, t: Union[float, np.array, Array]) -> Union[complex, np.array, Array]: + """Return the sum of the complex values of each component.""" + if Array.default_backend() == "jax": + t = Array(t) + exp_phases = np.exp(np.expand_dims(t, -1) * self._carrier_arg + self._phase_arg) + return np.sum(self.envelope(t) * exp_phases, axis=-1)
+ + + def __str__(self): + if self.name is not None: + return str(self.name) + + if len(self) == 0: + return "SignalSum()" + + default_str = str(self[0]) + for sig in self.components[1:]: + default_str += f" + {str(sig)}" + + return default_str + +
+[docs] + def flatten(self) -> Signal: + r"""Merge into a single ``Signal``\. The output frequency is given by the + average. + """ + + if len(self) == 0: + return Signal(0.0) + elif len(self) == 1: + return self.components[0] + + ave_freq = np.sum(self.carrier_freq) / len(self) + shifted_arg = self._carrier_arg - (1j * 2 * np.pi * ave_freq) + + def merged_env(t): + exp_phases = np.exp(np.expand_dims(Array(t), -1) * shifted_arg + self._phase_arg) + return np.sum(self.envelope(t) * exp_phases, axis=-1) + + return Signal(envelope=merged_env, carrier_freq=ave_freq, name=str(self))
+
+ + + +
+[docs] +class DiscreteSignalSum(DiscreteSignal, SignalSum): + """Represents a sum of piecewise constant signals, all with the same + time parameters: dt, number of samples, and start time. + """ + + def __init__( + self, + dt: float, + samples: Union[List, Array], + start_time: float = 0.0, + carrier_freq: Union[List, np.array, Array] = None, + phase: Union[List, np.array, Array] = None, + name: str = None, + ): + r"""Directly initialize a ``DiscreteSignalSum``\. Samples of all terms in the + sum are specified as a 2d array, with 0th axis indicating time, and 1st axis + indicating a term in the sum. + + Args: + dt: The duration of each sample. + samples: The 2d array representing a list whose elements are all envelope values + at a given time. + start_time: The time at which the signal starts. + carrier_freq: Array with the carrier frequencies of each term in the sum. + phase: Array with the phases of each term in the sum. + name: name of the signal. + """ + + samples = Array(samples) + if carrier_freq is None: + carrier_freq = np.zeros(samples.shape[-1], dtype=float) + + if phase is None: + phase = np.zeros(samples.shape[-1], dtype=float) + + DiscreteSignal.__init__( + self, + dt=dt, + samples=samples, + start_time=start_time, + carrier_freq=carrier_freq, + phase=phase, + name=name, + ) + + # construct individual components so they can be accessed as in SignalSum + components = [] + for sample_row, freq, phi in zip(self.samples.transpose(), carrier_freq, phase): + components.append( + DiscreteSignal( + dt=self.dt, + samples=sample_row, + start_time=self.start_time, + carrier_freq=freq, + phase=phi, + ) + ) + + self._components = components + +
+[docs] + @classmethod + def from_SignalSum( + cls, + signal_sum: SignalSum, + dt: float, + n_samples: int, + start_time: Optional[float] = 0.0, + sample_carrier: Optional[bool] = False, + ): + r"""Constructs a ``DiscreteSignalSum`` object by sampling a ``SignalSum``\. + + The optional argument ``sample_carrier`` controls whether or not to include the carrier + in the sampling. I.e.: + + - If ``sample_carrier == False``, a ``DiscreteSignalSum`` is constructed with: + - ``samples`` obtained by sampling ``signal_sum.envelope``\. + - ``carrier_freq = signal_sum.carrier_freq``\. + - ``phase = signal_sum.phase``\. + + - If ``sample_carrier == True``\, a ``DiscreteSignal`` is constructed with: + - ``samples`` obtained by sampling ``signal_sum`` (as a ``callable``\) + - ``carrier_freq = 0``\. + - ``phase = signal_sum.phase``\. + + In either case, samples are obtained from the midpoint of each interval. + + Args: + signal_sum: SignalSum to sample. + dt: Time increment to use. + n_samples: Number of steps to resample with. + start_time: Start time from which to resample. + sample_carrier: Whether or not to include the carrier in the sampling. + + Returns: + DiscreteSignalSum: The discretized ``SignalSum``\. + """ + + times = start_time + (np.arange(n_samples) + 0.5) * dt + + freq = signal_sum.carrier_freq + + if sample_carrier: + freq = 0.0 * freq + exp_phases = np.exp(np.expand_dims(Array(times), -1) * signal_sum._carrier_arg) + samples = signal_sum.envelope(times) * exp_phases + else: + samples = signal_sum.envelope(times) + + return DiscreteSignalSum( + dt, + samples, + start_time=start_time, + carrier_freq=freq, + phase=signal_sum.phase, + name=signal_sum.name, + )
+ + + def __str__(self): + """Get the string rep.""" + if self.name is not None: + return str(self.name) + + if len(self) == 0: + return "DiscreteSignalSum()" + + default_str = str(self[0]) + for sig in self.components[1:]: + default_str += f" + {str(sig)}" + + return default_str + + def __getitem__(self, idx: Union[int, List, np.array, slice]) -> Signal: + """Enables numpy-style subscripting, as if this class were a 1d array.""" + + if type(idx) == int and idx >= len(self): + raise IndexError("index out of range for DiscreteSignalSum of length " + str(len(self))) + + samples = self.samples[:, idx] + carrier_freqs = self.carrier_freq[idx] + phases = self.phase[idx] + + if samples.ndim == 1: + samples = Array([samples]) + + if carrier_freqs.ndim == 0: + carrier_freqs = Array([carrier_freqs]) + + if phases.ndim == 0: + phases = Array([phases]) + + if len(samples) == 1: + return DiscreteSignal( + dt=self.dt, + samples=samples[0], + start_time=self.start_time, + carrier_freq=carrier_freqs[0], + phase=phases[0], + ) + + return DiscreteSignalSum( + dt=self.dt, + samples=samples, + start_time=self.start_time, + carrier_freq=carrier_freqs, + phase=phases, + )
+ + + +
+[docs] +class SignalList(SignalCollection): + r"""A list of signals with functionality for simultaneous evaluation. + + The passed list is stored in the ``components`` attribute. + """ + + def __init__(self, signal_list: List[Signal]): + signal_list = [to_SignalSum(signal) for signal in signal_list] + + super().__init__(signal_list) + + # setup complex value and full signal evaluation + if Array.default_backend() == "jax": + self._eval_complex_value = array_funclist_evaluate( + [sig.complex_value for sig in self.components] + ) + self._eval_signals = array_funclist_evaluate(self.components) + else: + self._eval_complex_value = lambda t: [sig.complex_value(t) for sig in self.components] + self._eval_signals = lambda t: [sig(t) for sig in self.components] + +
+[docs] + def complex_value(self, t: Union[float, np.array, Array]) -> Union[np.array, Array]: + """Vectorized evaluation of complex value of components.""" + return np.moveaxis(self._eval_complex_value(t), 0, -1)
+ + + def __call__(self, t: Union[float, np.array, Array]) -> Union[np.array, Array]: + """Vectorized evaluation of all components.""" + return np.moveaxis(self._eval_signals(t), 0, -1) + +
+[docs] + def flatten(self) -> "SignalList": + """Return a ``SignalList`` with each component flattened.""" + flattened_list = [] + for sig in self.components: + if isinstance(sig, SignalSum): + flattened_list.append(sig.flatten()) + else: + flattened_list.append(sig) + + return SignalList(flattened_list)
+ + + @property + def drift(self) -> Array: + r"""Return the drift ``Array``\, i.e. return an ``Array`` whose entries are the sum + of the constant parts of the corresponding component of this ``SignalList``\. + """ + + drift_array = [] + for sig_entry in self.components: + val = 0.0 + + if not isinstance(sig_entry, SignalSum): + sig_entry = SignalSum(sig_entry) + + for term in sig_entry: + if term.is_constant: + val += Array(term(0.0)).data + + drift_array.append(val) + + return Array(drift_array)
+ + + +def signal_add(sig1: Signal, sig2: Signal) -> SignalSum: + """Add two signals.""" + + # generic routine + # convert to SignalSum instances + try: + sig1 = to_SignalSum(sig1) + sig2 = to_SignalSum(sig2) + except QiskitError as qe: + raise QiskitError("Only a number or a Signal instance can be added to a Signal.") from qe + + # if both are DiscreteSignalSum objects with compatible structure, append data together + if isinstance(sig1, DiscreteSignalSum) and isinstance(sig2, DiscreteSignalSum): + if ( + sig1.dt == sig2.dt + and sig1.start_time == sig2.start_time + and sig1.duration == sig2.duration + ): + samples = np.append(sig1.samples, sig2.samples, axis=1) + carrier_freq = np.append(sig1.carrier_freq, sig2.carrier_freq) + phase = np.append(sig1.phase, sig2.phase) + return DiscreteSignalSum( + dt=sig1.dt, + samples=samples, + start_time=sig1.start_time, + carrier_freq=carrier_freq, + phase=phase, + ) + + sig_sum = SignalSum(*(sig1.components + sig2.components)) + + return sig_sum + + +def signal_multiply(sig1: Signal, sig2: Signal) -> SignalSum: + r"""Multiply two ``Signal``\s. For a pair of elementary (non-``SignalSum``\) ``Signal``\s, + expands the product of two signals into a ``SignalSum`` via the formula: + + .. math:: + Re[f(t)e^{i(2 \pi \nu t + \phi)}] \times Re[g(t)e^{i(2 \pi \omega t + \psi)}] + = Re[\frac{1}{2} f(t)g(t)e^{i(2\pi (\omega + \nu)t + (\phi + \psi))} ] + + Re[\frac{1}{2} f(t)\overline{g(t)}e^{i(2\pi (\omega - \nu)t + (\phi - \psi))} ] + + If either (or both) of ``sig1`` or ``sig2`` are ``SignalSum``\s, the multiplication is + distributed over addition. + """ + + # convert to SignalSum instances + try: + sig1 = to_SignalSum(sig1) + sig2 = to_SignalSum(sig2) + except QiskitError as qe: + raise QiskitError("Only a number or a Signal instance can multiply a Signal.") from qe + + sig1, sig2 = sort_signals(sig1, sig2) + + # if sig1 contains only a constant and sig2 is a DiscreteSignalSum + if len(sig1) == 1 and sig1[0].is_constant and isinstance(sig2, DiscreteSignalSum): + return DiscreteSignalSum( + dt=sig2.dt, + samples=sig1(0.0) * sig2.samples, + start_time=sig2.start_time, + carrier_freq=sig2.carrier_freq, + phase=sig2.phase, + ) + # if both are DiscreteSignalSum objects with compatible structure, append data together + elif isinstance(sig1, DiscreteSignalSum) and isinstance(sig2, DiscreteSignalSum): + if ( + sig1.dt == sig2.dt + and sig1.start_time == sig2.start_time + and sig1.duration == sig2.duration + ): + # this vectorized operation produces a 2d array whose columns are the products of + # the original columns + new_samples = Array( + 0.5 + * (sig1.samples[:, :, None] * sig2.samples[:, None, :]).reshape( + (sig1.samples.shape[0], sig1.samples.shape[1] * sig2.samples.shape[1]), + order="C", + ) + ) + new_samples_conj = Array( + 0.5 + * (sig1.samples[:, :, None] * sig2.samples[:, None, :].conj()).reshape( + (sig1.samples.shape[0], sig1.samples.shape[1] * sig2.samples.shape[1]), + order="C", + ) + ) + samples = np.append(new_samples, new_samples_conj, axis=1) + + new_freqs = sig1.carrier_freq + sig2.carrier_freq + new_freqs_conj = sig1.carrier_freq - sig2.carrier_freq + freqs = np.append(Array(new_freqs), Array(new_freqs_conj)) + + new_phases = sig1.phase + sig2.phase + new_phases_conj = sig1.phase - sig2.phase + phases = np.append(Array(new_phases), Array(new_phases_conj)) + + return DiscreteSignalSum( + dt=sig1.dt, + samples=samples, + start_time=sig1.start_time, + carrier_freq=freqs, + phase=phases, + ) + + # initialize to empty sum + product = SignalSum() + + # loop through every pair of components and multiply + for comp1, comp2 in itertools.product(sig1.components, sig2.components): + product += base_signal_multiply(comp1, comp2) + + return product + + +def base_signal_multiply(sig1: Signal, sig2: Signal) -> Signal: + r"""Utility function for multiplying two elementary (non ``SignalSum``\) signals. + This function assumes ``sig1`` and ``sig2`` are legitimate instances of ``Signal`` + subclasses. + + Special cases: + + - Multiplication of two constant ``Signal``\s returns a constant ``Signal``\. + - Multiplication of a constant ``Signal`` and a ``DiscreteSignal`` returns + a ``DiscreteSignal``\. + - If two ``DiscreteSignal``\s have compatible parameters, the resulting signals are + ``DiscreteSignal``\, with the multiplication being implemented by array multiplication of + the samples. + - Lastly, if no special rules apply, the two ``Signal``\s are multiplied generically via + multiplication of the envelopes as functions. + + When a sum with two signals is produced, the carrier frequency (phase) of each component are, + respectively, the sum and difference of the two frequencies (phases). For special cases + involving constant ``Signal``\s and a non-constant ``Signal``, the carrier frequency and phase + are preserved as that of the non-constant piece. + + Args: + sig1: First signal. + sig2: Second signal. + + Returns: + SignalSum: Representing the RHS of the formula when two Signals are multiplied. + """ + + # ensure signals are ordered from most to least specialized + sig1, sig2 = sort_signals(sig1, sig2) + + if sig1.is_constant and sig2.is_constant: + return Signal(sig1(0.0) * sig2(0.0)) + elif sig1.is_constant and type(sig2) is DiscreteSignal: + # multiply the samples by the constant + return DiscreteSignal( + dt=sig2.dt, + samples=sig1(0.0) * sig2.samples, + start_time=sig2.start_time, + carrier_freq=sig2.carrier_freq, + phase=sig2.phase, + ) + elif sig1.is_constant and type(sig2) is Signal: + const = sig1(0.0) + + def new_env(t): + return const * sig2.envelope(t) + + return Signal(envelope=new_env, carrier_freq=sig2.carrier_freq, phase=sig2.phase) + elif type(sig1) is DiscreteSignal and type(sig2) is DiscreteSignal: + # verify compatible parameters before applying special rule + if ( + sig1.start_time == sig2.start_time + and sig1.dt == sig2.dt + and len(sig1.samples) == len(sig2.samples) + ): + pwc1 = DiscreteSignal( + dt=sig2.dt, + samples=0.5 * sig1.samples * sig2.samples, + start_time=sig2.start_time, + carrier_freq=sig1.carrier_freq + sig2.carrier_freq, + phase=sig1.phase + sig2.phase, + ) + pwc2 = DiscreteSignal( + dt=sig2.dt, + samples=0.5 * sig1.samples * np.conjugate(sig2.samples), + start_time=sig2.start_time, + carrier_freq=sig1.carrier_freq - sig2.carrier_freq, + phase=sig1.phase - sig2.phase, + ) + return pwc1 + pwc2 + + # if no special cases apply, implement generic rule + def new_env1(t): + return 0.5 * sig1.envelope(t) * sig2.envelope(t) + + def new_env2(t): + return 0.5 * sig1.envelope(t) * np.conjugate(sig2.envelope(t)) + + prod1 = Signal( + envelope=new_env1, + carrier_freq=sig1.carrier_freq + sig2.carrier_freq, + phase=sig1.phase + sig2.phase, + ) + prod2 = Signal( + envelope=new_env2, + carrier_freq=sig1.carrier_freq - sig2.carrier_freq, + phase=sig1.phase - sig2.phase, + ) + return prod1 + prod2 + + +def sort_signals(sig1: Signal, sig2: Signal) -> Tuple[Signal, Signal]: + r"""Utility function for ordering a pair of ``Signal``\s according to the partial order: + ``sig1 <= sig2`` if and only if: + - ``type(sig1)`` precedes ``type(sig2)`` in the list + ``[constant, DiscreteSignal, Signal, SignalSum, DiscreteSignalSum]``\. + """ + if sig1.is_constant: + pass + elif sig2.is_constant: + sig1, sig2 = sig2, sig1 + elif isinstance(sig1, DiscreteSignal) and not isinstance(sig1, DiscreteSignalSum): + pass + elif isinstance(sig2, DiscreteSignal) and not isinstance(sig2, DiscreteSignalSum): + sig2, sig1 = sig1, sig2 + elif isinstance(sig1, Signal) and not isinstance(sig1, SignalSum): + pass + elif isinstance(sig2, Signal) and not isinstance(sig2, SignalSum): + sig2, sig1 = sig1, sig2 + elif isinstance(sig1, SignalSum) and not isinstance(sig1, DiscreteSignalSum): + pass + elif isinstance(sig2, SignalSum) and not isinstance(sig2, DiscreteSignalSum): + sig2, sig1 = sig1, sig2 + elif isinstance(sig1, DiscreteSignalSum): + pass + elif isinstance(sig2, DiscreteSignalSum): + sig2, sig1 = sig1, sig2 + + return sig1, sig2 + + +def to_SignalSum(sig: Union[int, float, complex, Array, Signal]) -> SignalSum: + r"""Convert the input to a SignalSum according to: + + - If it is already a ``SignalSum``\, do nothing. + - If it is a Signal but not a ``SignalSum``\, wrap in a ``SignalSum``\. + - If it is a number, wrap in constant ``Signal`` in a ``SignalSum``\. + - Otherwise, raise an error. + + Args: + sig: A SignalSum compatible input. + + Returns: + SignalSum + + Raises: + QiskitError: If the input type is incompatible with SignalSum. + """ + + if isinstance(sig, (int, float, complex)) or (isinstance(sig, Array) and sig.ndim == 0): + return SignalSum(Signal(sig)) + elif isinstance(sig, DiscreteSignal) and not isinstance(sig, DiscreteSignalSum): + if Array(sig.samples.data).shape == (0,): + new_samples = Array([sig.samples.data]) + else: + new_samples = Array([sig.samples.data]).transpose(1, 0) + return DiscreteSignalSum( + dt=sig.dt, + samples=new_samples, + start_time=sig.start_time, + carrier_freq=Array([sig.carrier_freq.data]), + phase=Array([sig.phase.data]), + ) + elif isinstance(sig, Signal) and not isinstance(sig, SignalSum): + return SignalSum(sig) + elif isinstance(sig, SignalSum): + return sig + + raise QiskitError("Input type incompatible with SignalSum.") + + +def array_funclist_evaluate(func_list: List[Callable]) -> Callable: + """Utility for evaluating a list of functions in a way that respects Arrays. + Currently relevant for JAX evaluation. + """ + + def eval_func(t): + return Array([Array(func(t)).data for func in func_list]) + + return eval_func +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/signals/transfer_functions.html b/stable/0.4/_modules/qiskit_dynamics/signals/transfer_functions.html new file mode 100644 index 000000000..82ff60955 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/signals/transfer_functions.html @@ -0,0 +1,578 @@ + + + + + + + + qiskit_dynamics.signals.transfer_functions - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.signals.transfer_functions

+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Module for transformations between signals.
+"""
+
+from abc import ABC, abstractmethod
+from typing import Callable, Union, List
+import numpy as np
+
+from qiskit import QiskitError
+from qiskit_dynamics.array import Array
+
+from .signals import Signal, DiscreteSignal
+
+
+class BaseTransferFunction(ABC):
+    """Base class for transforming signals."""
+
+    @property
+    @abstractmethod
+    def n_inputs(self):
+        """Number of input signals to the transfer function."""
+        pass
+
+    def __call__(self, *args, **kwargs) -> Union[Signal, List[Signal]]:
+        """
+        Apply the transfer function to the input signals.
+
+        Args:
+            *args: The signals to which the transfer function will be applied.
+            **kwargs: Key word arguments to control the transfer functions.
+
+        Returns:
+            Signal: The transformed signal.
+
+        Raises:
+            QiskitError: if the number of args is not correct.
+        """
+
+        if len(args) != self.n_inputs:
+            raise QiskitError(
+                f"""{self.__class__.__name__} expected {len(args)}
+                input signals but {self.n_inputs} were given."""
+            )
+
+        return self._apply(*args, **kwargs)
+
+    @abstractmethod
+    def _apply(self, *args, **kwargs) -> Union[Signal, List[Signal]]:
+        """
+        Applies a transformation on a signal, such as a convolution,
+        low pass filter, etc.
+
+        Args:
+            *args: The signals to which the transfer function will be applied.
+            **kwargs: Key word arguments to control the transfer functions.
+
+        Returns:
+            Signal: The transformed signal.
+        """
+        pass
+
+
+
+[docs] +class Convolution(BaseTransferFunction): + """Applies a convolution as a sum + + (f*g)(n) = sum_k f(k)g(n-k) + + The implementation is quadratic in the number of samples in the signal. + """ + + def __init__(self, func: Callable): + """ + Args: + func: The convolution function specified in time. + This function will be normalized to one before doing + the convolution. To scale signals multiply them by a float. + """ + self._func = func + + @property + def n_inputs(self): + return 1 + + # pylint: disable=arguments-differ + def _apply(self, signal: Signal) -> Signal: + """ + Applies a transformation on a signal, such as a convolution, + low pass filter, etc. Once a convolution is applied the signal + can longer have a carrier as the carrier is part of the signal + value and gets convolved. + + Args: + signal: A signal or list of signals to which the + transfer function will be applied. + + Returns: + signal: The transformed signal or list of signals. + + Raises: + QiskitError: if the signal is not pwc. + """ + if isinstance(signal, DiscreteSignal): + # Perform a discrete time convolution. + dt = signal.dt + func_samples = Array([self._func(dt * i) for i in range(signal.duration)]) + func_samples = func_samples / sum(func_samples) + sig_samples = signal(dt * np.arange(signal.duration)) + + convoluted_samples = list(np.convolve(func_samples, sig_samples)) + + return DiscreteSignal(dt, convoluted_samples, carrier_freq=0.0, phase=0.0) + else: + raise QiskitError("Transfer function not defined on input.")
+ + + +class FFTConvolution(BaseTransferFunction): + """ + Applies a convolution by moving into the fourier domain. + """ + + def __init__(self, func: Callable): + self._func = func + + @property + def n_inputs(self): + return 1 + + # pylint: disable=arguments-differ + def _apply(self, signal: Signal) -> Signal: + raise NotImplementedError + + +class Sampler(BaseTransferFunction): + """ + Re sample a signal by wrapping DiscreteSignal.from_Signal. + """ + + def __init__(self, dt: float, n_samples: int, start_time: float = 0): + """ + Args: + dt: The new sample period. + n_samples: number of samples to resample with. + start_time: start time from which to resample. + """ + self._dt = dt + self._n_samples = n_samples + self._start_time = start_time + + @property + def n_inputs(self): + """Number of input signals to the transfer function.""" + return 1 + + # pylint: disable=arguments-differ + def _apply(self, signal: Signal) -> Signal: + """Apply the transfer function to the signal.""" + return DiscreteSignal.from_Signal( + signal, dt=self._dt, n_samples=self._n_samples, start_time=self._start_time + ) + + +
+[docs] +class IQMixer(BaseTransferFunction): + """ + Implements an IQ Mixer. The IQ mixer takes as input three signals: + - in-phase signal: I cos(w_if t + phi_I) + - quadrature: Q cos(w_if t + phi_Q) + - local oscillator: K cos(w_lo t) + + In this implementation the local oscillator is specified by its frequency + w_lo and, without loss of generality we assume K = 1. Furthermore, we + require that the carrier frequency of the I and Q be identical. + + The output RF signal is defined by + + s_rf = I [cos(wp t + phi_I) + cos(wm t + phi_I)]/2 + + Q [cos(wp t + phi_Q - pi/2) + cos(wm t + phi_Q + pi/2)]/2 + + where wp = w_lo + w_if and wp = w_lo - w_if. + + The output of this transfer function will produce a piece-wise constant + that does not have a carrier frequency or phase. All information is in the + samples. Mixer imperfections are not included. + """ + + def __init__(self, lo: float): + """ + Args: + lo: The carrier of the IQ mixer. + """ + self._lo = lo + + @property + def n_inputs(self): + return 2 + + # pylint: disable=arguments-differ + def _apply(self, si: Signal, sq: Signal) -> Signal: + """ + Args: + si: In phase signal + sq: Quadrature signal. + + Returns: + The up-converted signal. + + Raises: + QiskitError: if the carriers frequencies of I and Q differ. + """ + + # Check compatibility of the input signals + if si.carrier_freq != sq.carrier_freq: + raise QiskitError("IQ mixer requires the same sideband frequencies for I and Q.") + + phi_i, phi_q = si.phase, sq.phase + wp, wm = self._lo + si.carrier_freq, self._lo - si.carrier_freq + wp *= 2 * np.pi + wm *= 2 * np.pi + + def mixer_func(t): + """Function of the IQ mixer.""" + osc_i = np.cos(wp * t + phi_i) + np.cos(wm * t + phi_i) + osc_q = np.cos(wp * t + phi_q - np.pi / 2) + np.cos(wm * t + phi_q + np.pi / 2) + return si.envelope(t) * osc_i / 2 + sq.envelope(t) * osc_q / 2 + + return Signal(mixer_func, carrier_freq=0, phase=0)
+ +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/solvers/perturbative_solvers/dyson_solver.html b/stable/0.4/_modules/qiskit_dynamics/solvers/perturbative_solvers/dyson_solver.html new file mode 100644 index 000000000..4fd455c61 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/solvers/perturbative_solvers/dyson_solver.html @@ -0,0 +1,532 @@ + + + + + + + + qiskit_dynamics.solvers.perturbative_solvers.dyson_solver - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.solvers.perturbative_solvers.dyson_solver

+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2022.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Dyson series based solver.
+"""
+
+from typing import Optional, List, Union
+
+from scipy.integrate._ivp.ivp import OdeResult
+
+from multiset import Multiset
+
+from qiskit.quantum_info import Operator
+
+from qiskit_dynamics import Signal, RotatingFrame
+from qiskit_dynamics.array import Array
+
+from .expansion_model import ExpansionModel
+from .perturbative_solver import _PerturbativeSolver, _perturbative_solve, _perturbative_solve_jax
+
+
+
+[docs] +class DysonSolver(_PerturbativeSolver): + r"""Solver for linear matrix differential equations based on the Dyson series. + + This class implements the Dyson-series based solver presented in + [:footcite:`puzzuoli_sensitivity_2022`], which is a variant of the *Dysolve* algorithm + originally introduced in [:footcite:p:`shillito_fast_2020`]. + This solver applies to linear matrix differential equations with generators of the form: + + .. math:: + + G(t) = G_0 + \sum_{j=1}^s \textnormal{Re}[f_j(t) e^{i 2 \pi \nu_j t}]G_j, + + and solves the LMDE in the rotating frame of :math:`G_0`, which is assumed to be + anti-Hermitian. I.e. it solves the LMDE with generator: + + .. math:: + + \tilde{G}(t) = \sum_{j=1}^s \textnormal{Re}[f_j(t) e^{i 2 \pi \nu_j t}]\tilde{G}_j(t), + + with :math:`\tilde{G}_i(t) = e^{-t G_0} G_i e^{tG_0}`. The solver is *fixed-step*, + with step size :math:`\Delta t` being defined at instantiation, + and solves over each step by computing a truncated Dyson series. See the + :ref:`Time-dependent perturbation theory and multi-variable + series expansions review <perturbation review>` for a description of the Dyson series. + + At instantiation, the following parameters, which define the structure of the Dyson series + used, are fixed: + + - The step size :math:`\Delta t`, + - The operator structure :math:`G_0`, :math:`G_i`, + - The reference frequencies :math:`\nu_j`, + - Approximation schemes for the envelopes :math:`f_j` over each time step (see below), and + - The Dyson series terms to keep in the truncation. + + A 'compilation' or 'pre-computation' step computing the truncated expansion occurs + at instantiation. Once instantiated, the LMDE can be solved repeatedly for different lists of + envelopes :math:`f_1(t), \dots, f_s(t)` by calling the :meth:`solve` method with the + initial time ``t0`` and number of time-steps ``n_steps`` of size :math:`\Delta t`. + The list of envelopes are specified as :class:`~qiskit_dynamics.signals.Signal` objects, + whose carrier frequencies are automatically shifted to the reference frequencies :math:`\nu_j`, + with the frequency difference, and any phase, being absorbed into the envelope. + + More explicitly, the process of solving over an interval :math:`[t_0, t_0 + \Delta t]` + is as follows. After shifting the carrier frequencies, the resulting envelopes are approximated + using a discrete Chebyshev transform, whose orders for each signal is given by + ``chebyshev_orders``. That is, for :math:`t \in [t_0, t_0 + \Delta t]`, each envelope is + approximated as: + + .. math:: + + f_j(t) \approx \sum_{m=0}^{d_j} f_{j,m}T_m(t-t_0) + + where :math:`T_m(\cdot)` are the Chebyshev polynomials over the interval, + :math:`f_{j,m}` are the approximation coefficients attained via Discrete Chebyshev Transform, + and :math:`d_j` is the order of approximation used for the given envelope. + Using: + + .. math:: + + \textnormal{Re}[f_{j,m}T_m(t-t_0)e^{i2 \pi \nu_j t}] = + \textnormal{Re}[f_{j,m}e^{i 2 \pi \nu_j t_0}] \cos(2 \pi \nu_j (t-t_0))T_m(t-t_0) \\ + + \textnormal{Im}[f_{j,m}e^{i 2 \pi \nu_j t_0}] \sin(-2 \pi \nu_j (t-t_0))T_m(t-t_0) + + The generator is approximately decomposed as + + .. math:: + + \tilde{G}(t) \approx \sum_{j=1}^s \sum_{m=0}^{d_j} + \textnormal{Re}[f_{j,m}e^{i 2 \pi \nu_j t_0}] + \cos(2 \pi \nu_j (t-t_0))T_m(t-t_0) \tilde{G}_j \\ + + \sum_{j=1}^s \sum_{m=0}^{d_j} + \textnormal{Im}[f_{j,m}e^{i 2 \pi \nu_j t_0}] + \sin(- 2 \pi \nu_j (t-t_0))T_m(t-t_0) \tilde{G}_j + + The multivariable Dyson series is then computed relative to the + above decomposition, with the variables being the + :math:`\textnormal{Re}[f_{j,m}e^{i 2 \pi \nu_j t_0}]` and + :math:`\textnormal{Im}[f_{j,m}e^{i 2 \pi \nu_j t_0}]`, and the operators being + :math:`\cos(2 \pi \nu_j (t-t_0))T_m(t-t_0) G_j` and + :math:`\sin(- 2 \pi \nu_j (t-t_0))T_m(t-t_0) G_j`. As shown in + [:footcite:`puzzuoli_sensitivity_2022`, :footcite:p:`shillito_fast_2020`], + the multivariable Dyson series for intervals of length :math:`\Delta t` + with different starting times are related via a simple frame change, and as such + these need only be computed once, and this makes up the 'pre-computation' step of this + object. + + The optional argument ``include_imag`` can be used to control, on a signal by signal basis, + whether or not the imaginary terms + + .. math:: + + \textnormal{Im}[f_{j,m}e^{i 2 \pi \nu_j t_0}] + \sin(- 2 \pi \nu_j (t-t_0))T_m(t-t_0) \tilde{G}_j + + are included in the scheme. In generality they are required, but in special cases they are not + necessary, such as when :math:`\nu_j = 0`, or if :math:`f_j(t)`, including phase, is purely + real. By default all such terms are included. + + .. footbibliography:: + """ + + def __init__( + self, + operators: List[Operator], + rotating_frame: Union[Array, Operator, RotatingFrame, None], + dt: float, + carrier_freqs: Array, + chebyshev_orders: List[int], + expansion_order: Optional[int] = None, + expansion_labels: Optional[List[Multiset]] = None, + integration_method: Optional[str] = None, + include_imag: Optional[List[bool]] = None, + **kwargs, + ): + r"""Initialize. + + Args: + operators: List of constant operators specifying the operators with signal coefficients. + rotating_frame: Rotating frame to setup the solver in. + Must be Hermitian or anti-Hermitian. + dt: Fixed step size to compile to. + carrier_freqs: Carrier frequencies of the signals in the generator decomposition. + chebyshev_orders: Approximation degrees for each signal over the interval [0, dt]. + expansion_order: Order of perturbation terms to compute up to. Specifying this + argument results in computation of all terms up to the given order. + Can be used in conjunction with ``expansion_terms``. + expansion_labels: Specific perturbation terms to compute. If both ``expansion_order`` + and ``expansion_terms`` are specified, then all terms up to + ``expansion_order`` are computed, along with the additional terms + specified in ``expansion_terms``. Labels are specified either as + ``Multiset`` or as valid arguments to the ``Multiset`` constructor. + This function further requires that ``Multiset``\s consist only of + non-negative integers. + integration_method: ODE solver method to use when computing perturbation terms. + include_imag: List of bools determining whether to keep imaginary components in + the signal approximation. Defaults to True for all signals. + kwargs: Additional arguments to pass to the solver when computing perturbation terms. + """ + model = ExpansionModel( + operators=operators, + rotating_frame=rotating_frame, + dt=dt, + carrier_freqs=carrier_freqs, + chebyshev_orders=chebyshev_orders, + expansion_method="dyson", + expansion_order=expansion_order, + expansion_labels=expansion_labels, + integration_method=integration_method, + include_imag=include_imag, + **kwargs, + ) + super().__init__(model=model) + + def _solve(self, t0: float, n_steps: int, y0: Array, signals: List[Signal]) -> OdeResult: + ys = None + if Array.default_backend() == "jax": + single_step = lambda x: self.model.evaluate(x).data + ys = [y0, _perturbative_solve_jax(single_step, self.model, signals, y0, t0, n_steps)] + else: + single_step = lambda coeffs, y: self.model.evaluate(coeffs) @ y + ys = [y0, _perturbative_solve(single_step, self.model, signals, y0, t0, n_steps)] + + return OdeResult(t=[t0, t0 + n_steps * self.model.dt], y=ys)
+ +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/solvers/perturbative_solvers/magnus_solver.html b/stable/0.4/_modules/qiskit_dynamics/solvers/perturbative_solvers/magnus_solver.html new file mode 100644 index 000000000..36fbcc58f --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/solvers/perturbative_solvers/magnus_solver.html @@ -0,0 +1,452 @@ + + + + + + + + qiskit_dynamics.solvers.perturbative_solvers.magnus_solver - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.solvers.perturbative_solvers.magnus_solver

+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2022.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Magnus expansion-based solver.
+"""
+
+from typing import Optional, List, Union
+
+from scipy.linalg import expm
+from scipy.integrate._ivp.ivp import OdeResult
+
+from multiset import Multiset
+
+from qiskit.quantum_info import Operator
+
+from qiskit_dynamics import Signal, RotatingFrame
+from qiskit_dynamics.array import Array
+
+from .expansion_model import ExpansionModel
+from .perturbative_solver import _PerturbativeSolver, _perturbative_solve, _perturbative_solve_jax
+
+try:
+    from jax.scipy.linalg import expm as jexpm
+except ImportError:
+    pass
+
+
+
+[docs] +class MagnusSolver(_PerturbativeSolver): + """Solver for linear matrix differential equations based on the Magnus expansion. + + This class implements the Magnus expansion-based solver presented in + [:footcite:`puzzuoli_sensitivity_2022`], which is a Magnus expansion variant of the + *Dysolve* algorithm originally introduced in [:footcite:p:`shillito_fast_2020`]. Its + setup and behaviour are the same as as the :class:`~qiskit_dynamics.solvers.DysonSolver` + class, with the sole exception being that it uses a truncated Magnus expansion + and matrix exponentiation to solve over a single time step. See the + :ref:`Time-dependent perturbation theory and multi-variable + series expansions review <perturbation review>` for a description of the Magnus expansion, + and the documentation for :class:`~qiskit_dynamics.solvers.DysonSolver` for more detailed + behaviour of this class. + """ + + def __init__( + self, + operators: List[Operator], + rotating_frame: Union[Array, Operator, RotatingFrame, None], + dt: float, + carrier_freqs: Array, + chebyshev_orders: List[int], + expansion_order: Optional[int] = None, + expansion_labels: Optional[List[Multiset]] = None, + integration_method: Optional[str] = None, + include_imag: Optional[List[bool]] = None, + **kwargs, + ): + r"""Initialize. + + Args: + operators: List of constant operators specifying the operators with signal coefficients. + rotating_frame: Rotating frame to setup the solver in. + Must be Hermitian or anti-Hermitian. + dt: Fixed step size to compile to. + carrier_freqs: Carrier frequencies of the signals in the generator decomposition. + chebyshev_orders: Approximation degrees for each signal over the interval [0, dt]. + expansion_order: Order of perturbation terms to compute up to. Specifying this + argument results in computation of all terms up to the given order. + Can be used in conjunction with ``expansion_terms``. + expansion_labels: Specific perturbation terms to compute. If both ``expansion_order`` + and ``expansion_terms`` are specified, then all terms up to + ``expansion_order`` are computed, along with the additional terms + specified in ``expansion_terms``. Labels are specified either as + ``Multiset`` or as valid arguments to the ``Multiset`` constructor. + This function further requires that ``Multiset``\s consist only of + non-negative integers. + integration_method: ODE solver method to use when computing perturbation terms. + include_imag: List of bools determining whether to keep imaginary components in + the signal approximation. Defaults to True for all signals. + kwargs: Additional arguments to pass to the solver when computing perturbation terms. + """ + model = ExpansionModel( + operators=operators, + rotating_frame=rotating_frame, + dt=dt, + carrier_freqs=carrier_freqs, + chebyshev_orders=chebyshev_orders, + expansion_method="magnus", + expansion_order=expansion_order, + expansion_labels=expansion_labels, + integration_method=integration_method, + include_imag=include_imag, + **kwargs, + ) + super().__init__(model=model) + + def _solve(self, t0: float, n_steps: int, y0: Array, signals: List[Signal]) -> OdeResult: + ys = None + if Array.default_backend() == "jax": + single_step = lambda x: self.model.Udt @ jexpm(self.model.evaluate(x).data) + ys = [y0, _perturbative_solve_jax(single_step, self.model, signals, y0, t0, n_steps)] + else: + single_step = lambda coeffs, y: self.model.Udt @ expm(self.model.evaluate(coeffs)) @ y + ys = [y0, _perturbative_solve(single_step, self.model, signals, y0, t0, n_steps)] + + return OdeResult(t=[t0, t0 + n_steps * self.model.dt], y=ys)
+ +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/solvers/perturbative_solvers/perturbative_solver.html b/stable/0.4/_modules/qiskit_dynamics/solvers/perturbative_solvers/perturbative_solver.html new file mode 100644 index 000000000..3f6da9dfc --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/solvers/perturbative_solvers/perturbative_solver.html @@ -0,0 +1,565 @@ + + + + + + + + qiskit_dynamics.solvers.perturbative_solvers.perturbative_solver - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.solvers.perturbative_solvers.perturbative_solver

+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2022.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+"""
+Perturbative solver base class.
+"""
+
+from abc import ABC, abstractmethod
+from typing import Callable, List, Union
+
+import numpy as np
+from scipy.integrate._ivp.ivp import OdeResult
+
+from qiskit import QiskitError
+
+from qiskit_dynamics import Signal
+from qiskit_dynamics.signals import SignalList
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.dispatch.dispatch import Dispatch
+
+from .expansion_model import ExpansionModel
+from ..solver_utils import setup_args_lists
+
+try:
+    import jax.numpy as jnp
+    from jax import vmap
+    from jax.lax import associative_scan
+except ImportError:
+    pass
+
+
+class _PerturbativeSolver(ABC):
+    """Abstract base class for perturbation-based solvers."""
+
+    @abstractmethod
+    def __init__(self, model: ExpansionModel):
+        r"""Initialize.
+
+        Args:
+            model: ExpansionModel defining solver behaviour.
+        """
+        self._model = model
+
+    @property
+    def model(self) -> ExpansionModel:
+        """Model object storing expansion details."""
+        return self._model
+
+    def solve(
+        self,
+        t0: Union[float, List[float]],
+        n_steps: Union[int, List[int]],
+        y0: Union[Array, List[Array]],
+        signals: Union[List[Signal], List[List[Signal]]],
+    ) -> Union[OdeResult, List[OdeResult]]:
+        """Solve given an initial time, number of steps, signals, and initial state.
+
+        Note that this method can be used to solve a list of simulations at once, by specifying
+        one or more of the arguments ``t0``, ``n_steps``, ``y0``, or ``signals`` as a list of
+        valid inputs. For this mode of operation, all of these arguments must be either lists of
+        the same length, or a single valid input, which will be used repeatedly.
+
+        Args:
+            t0: Initial time.
+            n_steps: Number of time steps to solve for.
+            y0: Initial state at time ``t0``.
+            signals: List of signals.
+
+        Returns:
+            OdeResult: Results object, or list of results objects.
+
+        Raises:
+            QiskitError: If improperly formatted arguments.
+        """
+
+        # validate and setup list of simulations
+        [t0_list, n_steps_list, y0_list, signals_list], multiple_sims = setup_args_lists(
+            args_list=[t0, n_steps, y0, signals],
+            args_names=["t0", "n_steps", "y0", "signals"],
+            args_to_list=[
+                lambda x: _scalar_to_list(x, "t0"),
+                lambda x: _scalar_to_list(x, "n_steps"),
+                _y0_to_list,
+                _signals_to_list,
+            ],
+        )
+
+        all_results = []
+        for args in zip(t0_list, n_steps_list, y0_list, signals_list):
+            if len(args[-1]) != len(self.model.operators):
+                raise QiskitError("Signals must be the same length as the operators in the model.")
+            all_results.append(
+                self._solve(t0=args[0], n_steps=args[1], y0=args[2], signals=args[3])
+            )
+
+        if multiple_sims is False:
+            return all_results[0]
+
+        return all_results
+
+    @abstractmethod
+    def _solve(self, t0: float, n_steps: int, y0: Array, signals: List[Signal]) -> OdeResult:
+        """Solve once for a list of signals, initial state, initial time, and number of steps.
+
+        Args:
+            t0: Initial time.
+            n_steps: Number of time steps to solve for.
+            y0: Initial state at time t0.
+            signals: List of signals.
+
+        Returns:
+            OdeResult: Results object.
+        """
+        pass
+
+
+def _perturbative_solve(
+    single_step: Callable,
+    model: "ExpansionModel",
+    signals: List[Signal],
+    y0: np.ndarray,
+    t0: float,
+    n_steps: int,
+) -> np.ndarray:
+    """Standard python logic version of perturbative solving routine."""
+    U0 = model.rotating_frame.state_out_of_frame(t0, np.eye(model.Udt.shape[0], dtype=complex))
+    Uf = model.rotating_frame.state_into_frame(
+        t0 + n_steps * model.dt, np.eye(U0.shape[0], dtype=complex)
+    )
+
+    sig_cheb_coeffs = model.approximate_signals(signals, t0, n_steps)
+
+    y = U0 @ y0
+    for k in range(n_steps):
+        y = single_step(sig_cheb_coeffs[:, k], y)
+
+    return Uf @ y
+
+
+def _perturbative_solve_jax(
+    single_step: Callable,
+    model: "ExpansionModel",
+    signals: List[Signal],
+    y0: np.ndarray,
+    t0: float,
+    n_steps: int,
+) -> np.ndarray:
+    """JAX version of _perturbative_solve."""
+    U0 = model.rotating_frame.state_out_of_frame(
+        t0, jnp.eye(model.Udt.shape[0], dtype=complex)
+    ).data
+    Uf = model.rotating_frame.state_into_frame(
+        t0 + n_steps * model.dt, jnp.eye(U0.shape[0], dtype=complex)
+    ).data
+
+    sig_cheb_coeffs = model.approximate_signals(signals, t0, n_steps).data
+
+    y = U0 @ y0
+
+    step_propagators = vmap(single_step)(jnp.flip(sig_cheb_coeffs.transpose(), axis=0))
+    y = associative_scan(jnp.matmul, step_propagators, axis=0)[-1] @ y
+
+    return Uf @ y
+
+
+def _scalar_to_list(x, name):
+    """Check if x is a scalar or a list of scalars, and convert to a list in either case."""
+    was_list = False
+    x_ndim = _nested_ndim(x)
+    if x_ndim > 1:
+        raise QiskitError(f"{name} must be either 0d or 1d.")
+
+    if x_ndim == 1:
+        was_list = True
+    else:
+        x = [x]
+
+    return x, was_list
+
+
+def _y0_to_list(y0):
+    """Check if y0 is a single array or list of arrays, and return as a list in either case."""
+    was_list = False
+    if not isinstance(y0, list):
+        y0 = [y0]
+    else:
+        was_list = True
+
+    return y0, was_list
+
+
+def _signals_to_list(signals):
+    """Check if signals is a single signal specification or a list of
+    such specifications, and return as a list in either case.
+    """
+    was_list = False
+    if signals is None:
+        signals = [signals]
+    elif isinstance(signals, list) and isinstance(signals[0], (list, SignalList)):
+        # multiple lists
+        was_list = True
+    elif isinstance(signals, SignalList) or (
+        isinstance(signals, list) and not isinstance(signals[0], (list, SignalList))
+    ):
+        # single signals list
+        signals = [signals]
+    else:
+        raise QiskitError("Signals specified in invalid format.")
+
+    return signals, was_list
+
+
+def _nested_ndim(x):
+    """Determine the 'ndim' of x, which could be composed of nested lists and array types."""
+    if isinstance(x, (list, tuple)):
+        return 1 + _nested_ndim(x[0])
+    elif issubclass(type(x), Dispatch.REGISTERED_TYPES) or isinstance(x, Array):
+        return x.ndim
+
+    # assume scalar
+    return 0
+
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/solvers/solver_classes.html b/stable/0.4/_modules/qiskit_dynamics/solvers/solver_classes.html new file mode 100644 index 000000000..9cacd47b9 --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/solvers/solver_classes.html @@ -0,0 +1,1248 @@ + + + + + + + + qiskit_dynamics.solvers.solver_classes - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.solvers.solver_classes

+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2021.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name
+
+r"""
+Solver classes.
+"""
+
+
+from typing import Optional, Union, Tuple, Any, Type, List, Callable
+
+import numpy as np
+
+from scipy.integrate._ivp.ivp import OdeResult  # pylint: disable=unused-import
+
+from qiskit import QiskitError
+from qiskit.pulse import Schedule, ScheduleBlock
+from qiskit.pulse.transforms import block_to_schedule
+
+from qiskit.circuit import Gate, QuantumCircuit
+from qiskit.quantum_info.operators.base_operator import BaseOperator
+from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel
+from qiskit.quantum_info.states.quantum_state import QuantumState
+from qiskit.quantum_info import SuperOp, Operator, DensityMatrix
+
+from qiskit_dynamics.models import (
+    HamiltonianModel,
+    LindbladModel,
+    RotatingFrame,
+    rotating_wave_approximation,
+)
+from qiskit_dynamics.signals import Signal, DiscreteSignal, SignalList
+from qiskit_dynamics.pulse import InstructionToSignals
+from qiskit_dynamics.array import Array
+from qiskit_dynamics.dispatch.dispatch import Dispatch
+
+from .solver_functions import solve_lmde, _is_diffrax_method
+from .solver_utils import (
+    is_lindblad_model_vectorized,
+    is_lindblad_model_not_vectorized,
+    setup_args_lists,
+)
+
+
+try:
+    from jax import core, jit
+    import jax.numpy as jnp
+except ImportError:
+    pass
+
+
+
+[docs] +class Solver: + r"""Solver class for simulating both Hamiltonian and Lindblad dynamics, with high + level type-handling of input states. + + If only Hamiltonian information is provided, this class will internally construct + a :class:`.HamiltonianModel` instance, and simulate the model + using the Schrodinger equation :math:`\dot{y}(t) = -iH(t)y(t)` + (see the :meth:`.Solver.solve` method documentation for details + on how different initial state types are handled). + :class:`.HamiltonianModel` represents a decomposition of the Hamiltonian of the form: + + .. math:: + + H(t) = H_0 + \sum_i s_i(t) H_i, + + where :math:`H_0` is the static component, the :math:`H_i` are the + time-dependent components of the Hamiltonian, and the :math:`s_i(t)` are the + time-dependent signals, specifiable as either :class:`.Signal` + objects, or constructed from Qiskit Pulse schedules if :class:`.Solver` + is configured for Pulse simulation (see below). + + If dissipators are specified as part of the model, then a + :class:`.LindbladModel` is constructed, and simulations are performed + by solving the Lindblad equation: + + .. math:: + + \dot{y}(t) = -i[H(t), y(t)] + \mathcal{D}_0(y(t)) + \mathcal{D}(t)(y(t)), + + where :math:`H(t)` is the Hamiltonian part, specified as above, and :math:`\mathcal{D}_0` + and :math:`\mathcal{D}(t)` are the static and time-dependent portions of the dissipator, + given by: + + .. math:: + + \mathcal{D}_0(y(t)) = \sum_j N_j y(t) N_j^\dagger + - \frac{1}{2} \{N_j^\dagger N_j, y(t)\}, + + and + + .. math:: + + \mathcal{D}(t)(y(t)) = \sum_j \gamma_j(t) L_j y(t) L_j^\dagger + - \frac{1}{2} \{L_j^\dagger L_j, y(t)\}, + + with :math:`N_j` the static dissipators, :math:`L_j` the time-dependent dissipator + operators, and :math:`\gamma_j(t)` the time-dependent signals + specifiable as either :class:`.Signal` + objects, or constructed from Qiskit Pulse schedules if :class:`.Solver` + is configured for Pulse simulation (see below). + + Transformations on the model can be specified via the optional arguments: + + * ``rotating_frame``: Transforms the model into a rotating frame. Note that the + operator specifying the frame will be substracted from the ``static_hamiltonian``. + If supplied as a 1d array, ``rotating_frame`` is interpreted as the diagonal + elements of a diagonal matrix. Given a frame operator :math:`F = -i H_0`, + for the Schrodinger equation entering the rotating frame of :math:`F`, corresponds + to transforming the solution as :math:`y(t) \mapsto exp(-tF)y(t)`, and for the + Lindblad equation it corresponds to transforming the solution as + :math:`y(t) \mapsto exp(-tF)y(t)exp(tF)`. + See :class:`.RotatingFrame` for more details. + * ``in_frame_basis``: Whether to represent the model in the basis in which the frame + operator is diagonal, henceforth called the "frame basis". + If ``rotating_frame`` is ``None`` or was supplied as a 1d array, + this kwarg has no effect. If ``rotating_frame`` was specified as a 2d array, + the frame basis is the diagonalizing basis supplied by ``np.linalg.eigh``. + If ``in_frame_basis==True``, this objects behaves as if all + operators were supplied in the frame basis: calls to ``solve`` will assume the initial + state is supplied in the frame basis, and the results will be returned in the frame basis. + If ``in_frame_basis==False``, the system will still be solved in the frame basis for + efficiency, however the initial state (and final output states) will automatically be + transformed into (and, respectively, out of) the frame basis. + * ``rwa_cutoff_freq`` and ``rwa_carrier_freqs``: Performs a rotating wave approximation (RWA) + on the model with cutoff frequency ``rwa_cutoff_freq``, assuming the time-dependent + coefficients of the model have carrier frequencies specified by ``rwa_carrier_freqs``. + If ``dissipator_operators is None``, ``rwa_carrier_freqs`` must be a list of floats + of length equal to ``hamiltonian_operators``, and if ``dissipator_operators is not None``, + ``rwa_carrier_freqs`` must be a ``tuple`` of lists of floats, with the first entry + the list of carrier frequencies for ``hamiltonian_operators``, and the second + entry the list of carrier frequencies for ``dissipator_operators``. + See :func:`.rotating_wave_approximation` for details on + the mathematical approximation. + + .. note:: + When using the ``rwa_cutoff_freq`` optional argument, + :class:`.Solver` cannot be instantiated within + a JAX-transformable function. However, after construction, instances can + still be used within JAX-transformable functions regardless of whether an + ``rwa_cutoff_freq`` is set. + + :class:`.Solver` can be configured to simulate Qiskit Pulse schedules + by setting all of the following parameters, which determine how Pulse schedules are + interpreted: + + * ``hamiltonian_channels``: List of channels in string format corresponding to the + time-dependent coefficients of ``hamiltonian_operators``. + * ``dissipator_channels``: List of channels in string format corresponding to time-dependent + coefficients of ``dissipator_operators``. + * ``channel_carrier_freqs``: Dictionary mapping channel names to frequencies. A frequency + must be specified for every channel appearing in ``hamiltonian_channels`` and + ``dissipator_channels``. When simulating ``schedule``\s, these frequencies are + interpreted as the analog carrier frequencies associated with the channel; deviations from + these frequencies due to ``SetFrequency`` or ``ShiftFrequency`` instructions are + implemented by digitally modulating the samples for the channel envelope. + If an ``rwa_cutoff_freq`` is specified, and no ``rwa_carrier_freqs`` is specified, these + frequencies will be used for the RWA. + * ``dt``: The envelope sample width. + + If configured to simulate Pulse schedules while ``Array.default_backend() == 'jax'``, + calling :meth:`.Solver.solve` will automatically compile + simulation runs when calling with a JAX-based solver method. + + The evolution given by the model can be simulated by calling :meth:`.Solver.solve`, which + calls :func:`.solve_lmde`, and does various automatic + type handling operations for :mod:`qiskit.quantum_info` state and super operator types. + """ + + def __init__( + self, + static_hamiltonian: Optional[Array] = None, + hamiltonian_operators: Optional[Array] = None, + static_dissipators: Optional[Array] = None, + dissipator_operators: Optional[Array] = None, + hamiltonian_channels: Optional[List[str]] = None, + dissipator_channels: Optional[List[str]] = None, + channel_carrier_freqs: Optional[dict] = None, + dt: Optional[float] = None, + rotating_frame: Optional[Union[Array, RotatingFrame]] = None, + in_frame_basis: bool = False, + evaluation_mode: str = "dense", + rwa_cutoff_freq: Optional[float] = None, + rwa_carrier_freqs: Optional[Union[Array, Tuple[Array, Array]]] = None, + validate: bool = True, + ): + """Initialize solver with model information. + + Args: + static_hamiltonian: Constant Hamiltonian term. If a ``rotating_frame`` + is specified, the ``frame_operator`` will be subtracted from + the static_hamiltonian. + hamiltonian_operators: Hamiltonian operators. + static_dissipators: Constant dissipation operators. + dissipator_operators: Dissipation operators with time-dependent coefficients. + hamiltonian_channels: List of channel names in pulse schedules corresponding to + Hamiltonian operators. + dissipator_channels: List of channel names in pulse schedules corresponding to + dissipator operators. + channel_carrier_freqs: Dictionary mapping channel names to floats which represent + the carrier frequency of the pulse channel with the + corresponding name. + dt: Sample rate for simulating pulse schedules. + rotating_frame: Rotating frame to transform the model into. Rotating frames which + are diagonal can be supplied as a 1d array of the diagonal elements, + to explicitly indicate that they are diagonal. + in_frame_basis: Whether to represent the model in the basis in which the rotating + frame operator is diagonalized. See class documentation for a more + detailed explanation on how this argument affects object behaviour. + evaluation_mode: Method for model evaluation. See documentation for + ``HamiltonianModel.evaluation_mode`` or + ``LindbladModel.evaluation_mode``. + (if dissipators in model) for valid modes. + rwa_cutoff_freq: Rotating wave approximation cutoff frequency. If ``None``, no + approximation is made. + rwa_carrier_freqs: Carrier frequencies to use for rotating wave approximation. + If no time dependent coefficients in model leave as ``None``, + if no time-dependent dissipators specify as a list of frequencies + for each Hamiltonian operator, and if time-dependent dissipators + present specify as a tuple of lists of frequencies, one for + Hamiltonian operators and one for dissipators. + validate: Whether or not to validate Hamiltonian operators as being Hermitian. + + Raises: + QiskitError: If arguments concerning pulse-schedule interpretation are insufficiently + specified. + """ + + # set pulse specific information if specified + self._hamiltonian_channels = None + self._dissipator_channels = None + self._all_channels = None + self._channel_carrier_freqs = None + self._dt = None + self._schedule_converter = None + + if any([dt, channel_carrier_freqs, hamiltonian_channels, dissipator_channels]): + all_channels = [] + + if hamiltonian_channels is not None: + hamiltonian_channels = [chan.lower() for chan in hamiltonian_channels] + if hamiltonian_operators is None or len(hamiltonian_operators) != len( + hamiltonian_channels + ): + raise QiskitError( + """hamiltonian_channels must have same length as hamiltonian_operators""" + ) + for chan in hamiltonian_channels: + if chan not in all_channels: + all_channels.append(chan) + + self._hamiltonian_channels = hamiltonian_channels + + if dissipator_channels is not None: + dissipator_channels = [chan.lower() for chan in dissipator_channels] + for chan in dissipator_channels: + if chan not in all_channels: + all_channels.append(chan) + if dissipator_operators is None or len(dissipator_operators) != len( + dissipator_channels + ): + raise QiskitError( + """dissipator_channels must have same length as dissipator_operators""" + ) + + self._dissipator_channels = dissipator_channels + self._all_channels = all_channels + + if channel_carrier_freqs is None: + channel_carrier_freqs = {} + else: + channel_carrier_freqs = { + key.lower(): val for key, val in channel_carrier_freqs.items() + } + + for chan in all_channels: + if chan not in channel_carrier_freqs: + raise QiskitError( + f"""Channel '{chan}' does not have carrier frequency specified in + channel_carrier_freqs.""" + ) + + if len(channel_carrier_freqs) == 0: + channel_carrier_freqs = None + self._channel_carrier_freqs = channel_carrier_freqs + + if dt is not None: + self._dt = dt + + self._schedule_converter = InstructionToSignals( + dt=self._dt, carriers=self._channel_carrier_freqs, channels=self._all_channels + ) + else: + raise QiskitError("dt must be specified if channel information is provided.") + + # setup model + model = None + if static_dissipators is None and dissipator_operators is None: + model = HamiltonianModel( + static_operator=static_hamiltonian, + operators=hamiltonian_operators, + rotating_frame=rotating_frame, + in_frame_basis=in_frame_basis, + evaluation_mode=evaluation_mode, + validate=validate, + ) + else: + model = LindbladModel( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=hamiltonian_operators, + static_dissipators=static_dissipators, + dissipator_operators=dissipator_operators, + rotating_frame=rotating_frame, + in_frame_basis=in_frame_basis, + evaluation_mode=evaluation_mode, + validate=validate, + ) + + self._rwa_signal_map = None + self._model = model + if rwa_cutoff_freq: + # if rwa_carrier_freqs is None, take from channel_carrier_freqs or set all to 0. + if rwa_carrier_freqs is None: + if self._channel_carrier_freqs is not None: + if self._hamiltonian_channels is not None: + rwa_carrier_freqs = [ + self._channel_carrier_freqs[c] for c in self._hamiltonian_channels + ] + + if self._dissipator_channels is not None: + rwa_carrier_freqs = ( + rwa_carrier_freqs, + [self._channel_carrier_freqs[c] for c in self._dissipator_channels], + ) + else: + rwa_carrier_freqs = [] + if hamiltonian_operators is not None: + rwa_carrier_freqs = [0.0] * len(hamiltonian_operators) + + if dissipator_operators is not None: + rwa_carrier_freqs = (rwa_carrier_freqs, [0.0] * len(dissipator_operators)) + + if isinstance(rwa_carrier_freqs, tuple): + rwa_ham_sigs = None + rwa_lindblad_sigs = None + if rwa_carrier_freqs[0]: + rwa_ham_sigs = [Signal(1.0, carrier_freq=freq) for freq in rwa_carrier_freqs[0]] + if rwa_carrier_freqs[1]: + rwa_lindblad_sigs = [ + Signal(1.0, carrier_freq=freq) for freq in rwa_carrier_freqs[1] + ] + + self._model.signals = (rwa_ham_sigs, rwa_lindblad_sigs) + else: + rwa_sigs = [Signal(1.0, carrier_freq=freq) for freq in rwa_carrier_freqs] + + if isinstance(model, LindbladModel): + rwa_sigs = (rwa_sigs, None) + + self._model.signals = rwa_sigs + + self._model, rwa_signal_map = rotating_wave_approximation( + self._model, rwa_cutoff_freq, return_signal_map=True + ) + self._rwa_signal_map = rwa_signal_map + + # clear signals + self._set_new_signals(None) + + @property + def model(self) -> Union[HamiltonianModel, LindbladModel]: + """The model of the system, either a Hamiltonian or Lindblad model.""" + return self._model + +
+[docs] + def solve( + self, + t_span: Array, + y0: Union[Array, QuantumState, BaseOperator], + signals: Optional[ + Union[ + List[Union[Schedule, ScheduleBlock]], + List[Signal], + Tuple[List[Signal], List[Signal]], + ] + ] = None, + convert_results: bool = True, + **kwargs, + ) -> Union[OdeResult, List[OdeResult]]: + r"""Solve a dynamical problem, or a set of dynamical problems. + + Calls :func:`.solve_lmde`, and returns an ``OdeResult`` + object in the style of ``scipy.integrate.solve_ivp``, with results + formatted to be the same types as the input. See Additional Information + for special handling of various input types, and for specifying multiple + simulations at once. + + Args: + t_span: Time interval to integrate over. + y0: Initial state. + signals: Specification of time-dependent coefficients to simulate, either in + Signal format or as Qiskit Pulse Pulse schedules. + If specifying in Signal format, if ``dissipator_operators is None``, + specify as a list of signals for the Hamiltonian component, otherwise + specify as a tuple of two lists, one for Hamiltonian components, and + one for the ``dissipator_operators`` coefficients. + convert_results: If ``True``, convert returned solver state results to the same class + as y0. If ``False``, states will be returned in the native array type + used by the specified solver method. + **kwargs: Keyword args passed to :func:`.solve_lmde`. + + Returns: + OdeResult: object with formatted output types. + + Raises: + QiskitError: Initial state ``y0`` is of invalid shape. If ``signals`` specifies + ``Schedule`` simulation but ``Solver`` hasn't been configured to + simulate pulse schedules. + + Additional Information: + + The behaviour of this method is impacted by the input type of ``y0`` + and the internal model, summarized in the following table: + + .. list-table:: Type-based behaviour + :widths: 10 10 10 70 + :header-rows: 1 + + * - ``y0`` type + - Model type + - ``yf`` type + - Description + * - ``Array``, ``np.ndarray``, ``Operator`` + - Any + - Same as ``y0`` + - Solves either the Schrodinger equation or Lindblad equation + with initial state ``y0`` as specified. + * - ``Statevector`` + - ``HamiltonianModel`` + - ``Statevector`` + - Solves the Schrodinger equation with initial state ``y0``. + * - ``DensityMatrix`` + - ``HamiltonianModel`` + - ``DensityMatrix`` + - Solves the Schrodinger equation with initial state the identity matrix to compute + the unitary, then conjugates ``y0`` with the result to solve for the density matrix. + * - ``Statevector``, ``DensityMatrix`` + - ``LindbladModel`` + - ``DensityMatrix`` + - Solve the Lindblad equation with initial state ``y0``, converting to a + ``DensityMatrix`` first if ``y0`` is a ``Statevector``. + * - ``QuantumChannel`` + - ``HamiltonianModel`` + - ``SuperOp`` + - Converts ``y0`` to a ``SuperOp`` representation, then solves the Schrodinger + equation with initial state the identity matrix to compute the unitary and + composes with ``y0``. + * - ``QuantumChannel`` + - ``LindbladModel`` + - ``SuperOp`` + - Solves the vectorized Lindblad equation with initial state ``y0``. + ``evaluation_mode`` must be set to a vectorized option. + + In some cases (e.g. if using JAX), wrapping the returned states in the type + given in the ``yf`` type column above may be undesirable. Setting + ``convert_results=False`` prevents this wrapping, while still allowing + usage of the automatic type-handling for the input state. + + In addition to the above, this method can be used to specify multiple simulations + simultaneously. This can be done by specifying one or more of the arguments + ``t_span``, ``y0``, or ``signals`` as a list of valid inputs. + For this mode of operation, all of these arguments must be either lists of the same + length, or a single valid input, which will be used repeatedly. + + For example the following code runs three simulations, returning results in a list: + + .. code-block:: python + + t_span = [span1, span2, span3] + y0 = [state1, state2, state3] + signals = [signals1, signals2, signals3] + + results = solver.solve(t_span=t_span, y0=y0, signals=signals) + + The following code block runs three simulations, for different sets of signals, + repeatedly using the same ``t_span`` and ``y0``: + + .. code-block:: python + + t_span = [t0, tf] + y0 = state1 + signals = [signals1, signals2, signal3] + + results = solver.solve(t_span=t_span, y0=y0, signals=signals) + """ + + # convert any ScheduleBlocks to Schedules + if isinstance(signals, ScheduleBlock): + signals = block_to_schedule(signals) + elif isinstance(signals, list): + signals = [block_to_schedule(x) if isinstance(x, ScheduleBlock) else x for x in signals] + + # validate and setup list of simulations + [t_span_list, y0_list, signals_list], multiple_sims = setup_args_lists( + args_list=[t_span, y0, signals], + args_names=["t_span", "y0", "signals"], + args_to_list=[t_span_to_list, _y0_to_list, _signals_to_list], + ) + + all_results = None + method = kwargs.get("method", "") + if ( + Array.default_backend() == "jax" + and (method == "jax_odeint" or _is_diffrax_method(method)) + and all(isinstance(x, Schedule) for x in signals_list) + # check if jit transformation is already performed. + and not (isinstance(jnp.array(0), core.Tracer)) + ): + all_results = self._solve_schedule_list_jax( + t_span_list=t_span_list, + y0_list=y0_list, + schedule_list=signals_list, + convert_results=convert_results, + **kwargs, + ) + else: + all_results = self._solve_list( + t_span_list=t_span_list, + y0_list=y0_list, + signals_list=signals_list, + convert_results=convert_results, + **kwargs, + ) + + # ensure model signals are empty + self._set_new_signals(None) + + if multiple_sims is False: + return all_results[0] + + return all_results
+ + + def _solve_list( + self, + t_span_list: List[Array], + y0_list: List[Union[Array, QuantumState, BaseOperator]], + signals_list: Optional[ + Union[List[Schedule], List[List[Signal]], List[Tuple[List[Signal], List[Signal]]]] + ] = None, + convert_results: bool = True, + **kwargs, + ) -> List[OdeResult]: + """Run a list of simulations.""" + + all_results = [] + for t_span, y0, signals in zip(t_span_list, y0_list, signals_list): + if isinstance(signals, Schedule): + signals = self._schedule_to_signals(signals) + + self._set_new_signals(signals) + + # setup initial state + y0, y0_input, y0_cls, state_type_wrapper = validate_and_format_initial_state( + y0, self.model + ) + + results = solve_lmde(generator=self.model, t_span=t_span, y0=y0, **kwargs) + results.y = format_final_states(results.y, self.model, y0_input, y0_cls) + + if y0_cls is not None and convert_results: + results.y = [state_type_wrapper(yi) for yi in results.y] + + all_results.append(results) + + self._set_new_signals(None) + + return all_results + + def _solve_schedule_list_jax( + self, + t_span_list: List[Array], + y0_list: List[Union[Array, QuantumState, BaseOperator]], + schedule_list: List[Schedule], + convert_results: bool = True, + **kwargs, + ) -> List[OdeResult]: + """Run a list of schedule simulations utilizing JAX compilation. + The jitting strategy is to define a function whose inputs are t_span, y0 as an array, the + samples for all channels in a single large array, and other initial state information. + To avoid recompilation for schedules with a different number of samples, i.e. a different + duration, all schedules are padded to be the length of the schedule with the max duration. + """ + + # determine fixed array shape for containing all samples + max_duration = 0 + for idx, sched in enumerate(schedule_list): + max_duration = max(sched.duration, max_duration) + all_samples_shape = (len(self._all_channels), max_duration) + + # define sim function to jit + def sim_function(t_span, y0, all_samples, y0_input, y0_cls): + # store signals to ensure purity + model_sigs = self.model.signals + + # re-construct signals from the samples + signals = [] + for idx, samples in enumerate(all_samples): + carrier_freq = self._channel_carrier_freqs[self._all_channels[idx]] + signals.append( + DiscreteSignal(dt=self._dt, samples=samples, carrier_freq=carrier_freq) + ) + + # map signals to correct structure for model + signals = organize_signals_to_channels( + signals, + self._all_channels, + self.model.__class__, + self._hamiltonian_channels, + self._dissipator_channels, + ) + + self._set_new_signals(signals) + + results = solve_lmde(generator=self.model, t_span=t_span, y0=y0, **kwargs) + results.y = format_final_states(results.y, self.model, y0_input, y0_cls) + + # reset signals to ensure purity + self.model.signals = model_sigs + + return Array(results.t).data, Array(results.y).data + + jit_sim_function = jit(sim_function, static_argnums=(4,)) + + # run simulations + all_results = [] + for t_span, y0, sched in zip(t_span_list, y0_list, schedule_list): + # setup initial state + y0, y0_input, y0_cls, state_type_wrapper = validate_and_format_initial_state( + y0, self.model + ) + + # setup array of all samples + all_signals = self._schedule_converter.get_signals(sched) + + all_samples = np.zeros(all_samples_shape, dtype=complex) + for idx, sig in enumerate(all_signals): + all_samples[idx, 0 : len(sig.samples)] = np.array(sig.samples) + + results_t, results_y = jit_sim_function( + Array(t_span).data, Array(y0).data, all_samples, Array(y0_input).data, y0_cls + ) + results = OdeResult(t=results_t, y=Array(results_y, backend="jax", dtype=complex)) + + if y0_cls is not None and convert_results: + results.y = [state_type_wrapper(yi) for yi in results.y] + + all_results.append(results) + + return all_results + + def _set_new_signals(self, signals): + """Helper function for setting new signals in self.model.""" + if signals is not None: + # if Lindblad model and signals are given as a list set as Hamiltonian part of signals + if isinstance(self.model, LindbladModel) and isinstance(signals, (list, SignalList)): + signals = (signals, None) + + if self._rwa_signal_map: + signals = self._rwa_signal_map(signals) + self.model.signals = signals + else: + if isinstance(self.model, LindbladModel): + self.model.signals = (None, None) + else: + self.model.signals = None + + def _schedule_to_signals(self, schedule: Schedule): + """Convert a schedule into the signal format required by the model.""" + if self._schedule_converter is None: + raise QiskitError("Solver instance not configured for pulse Schedule simulation.") + + return organize_signals_to_channels( + self._schedule_converter.get_signals(schedule), + self._all_channels, + self.model.__class__, + self._hamiltonian_channels, + self._dissipator_channels, + )
+ + + +def initial_state_converter(obj: Any) -> Tuple[Array, Type, Callable]: + """Convert initial state object to an Array, the type of the initial input, and return + function for constructing a state of the same type. + + Args: + obj: An initial state. + + Returns: + tuple: (Array, Type, Callable) + """ + # pylint: disable=invalid-name + y0_cls = None + if isinstance(obj, Array): + y0, y0_cls, wrapper = obj, None, lambda x: x + if isinstance(obj, QuantumState): + y0, y0_cls = Array(obj.data), obj.__class__ + wrapper = lambda x: y0_cls(np.array(x), dims=obj.dims()) + elif isinstance(obj, QuantumChannel): + y0, y0_cls = Array(SuperOp(obj).data), SuperOp + wrapper = lambda x: SuperOp( + np.array(x), input_dims=obj.input_dims(), output_dims=obj.output_dims() + ) + elif isinstance(obj, (BaseOperator, Gate, QuantumCircuit)): + y0, y0_cls = Array(Operator(obj.data)), Operator + wrapper = lambda x: Operator( + np.array(x), input_dims=obj.input_dims(), output_dims=obj.output_dims() + ) + else: + y0, y0_cls, wrapper = Array(obj), None, lambda x: x + + return y0, y0_cls, wrapper + + +def validate_and_format_initial_state(y0: any, model: Union[HamiltonianModel, LindbladModel]): + """Format initial state for simulation. This function encodes the logic of how + simulations are run based on initial state type. + + Args: + y0: The user-specified input state. + model: The model contained in the solver. + + Returns: + Tuple containing the input state to pass to the solver, the user-specified input + as an array, the class of the user specified input, and a function for converting + the output states to the right class. + + Raises: + QiskitError: Initial state ``y0`` is of invalid shape relative to the model. + """ + + if isinstance(y0, QuantumState) and isinstance(model, LindbladModel): + y0 = DensityMatrix(y0) + + y0, y0_cls, wrapper = initial_state_converter(y0) + + y0_input = y0 + + # validate types + if (y0_cls is SuperOp) and is_lindblad_model_not_vectorized(model): + raise QiskitError( + """Simulating SuperOp for a LindbladModel requires setting + vectorized evaluation. Set LindbladModel.evaluation_mode to a vectorized option. + """ + ) + + # if Simulating density matrix or SuperOp with a HamiltonianModel, simulate the unitary + if y0_cls in [DensityMatrix, SuperOp] and isinstance(model, HamiltonianModel): + y0 = np.eye(model.dim, dtype=complex) + # if LindbladModel is vectorized and simulating a density matrix, flatten + elif ( + (y0_cls is DensityMatrix) + and isinstance(model, LindbladModel) + and "vectorized" in model.evaluation_mode + ): + y0 = y0.flatten(order="F") + + # validate y0 shape before passing to solve_lmde + if isinstance(model, HamiltonianModel) and (y0.shape[0] != model.dim or y0.ndim > 2): + raise QiskitError("""Shape mismatch for initial state y0 and HamiltonianModel.""") + if is_lindblad_model_vectorized(model) and (y0.shape[0] != model.dim**2 or y0.ndim > 2): + raise QiskitError( + """Shape mismatch for initial state y0 and LindbladModel + in vectorized evaluation mode.""" + ) + if is_lindblad_model_not_vectorized(model) and y0.shape[-2:] != ( + model.dim, + model.dim, + ): + raise QiskitError("""Shape mismatch for initial state y0 and LindbladModel.""") + + return y0, y0_input, y0_cls, wrapper + + +def format_final_states(y, model, y0_input, y0_cls): + """Format final states for a single simulation.""" + + y = Array(y) + + if y0_cls is DensityMatrix and isinstance(model, HamiltonianModel): + # conjugate by unitary + return y @ y0_input @ y.conj().transpose((0, 2, 1)) + elif y0_cls is SuperOp and isinstance(model, HamiltonianModel): + # convert to SuperOp and compose + return ( + np.einsum("nka,nlb->nklab", y.conj(), y).reshape( + y.shape[0], y.shape[1] ** 2, y.shape[1] ** 2 + ) + @ y0_input + ) + elif (y0_cls is DensityMatrix) and is_lindblad_model_vectorized(model): + return y.reshape((len(y),) + y0_input.shape, order="F") + + return y + + +def t_span_to_list(t_span): + """Check if t_span is validly specified as a single interval or a list of intervals, + and return as a list in either case.""" + was_list = False + t_span_ndim = _nested_ndim(t_span) + if t_span_ndim > 2: + raise QiskitError("t_span must be either 1d or 2d.") + if t_span_ndim == 1: + t_span = [t_span] + else: + was_list = True + + return t_span, was_list + + +def _y0_to_list(y0): + """Check if y0 is validly specified as a single initial state or a list of initial states, + and return as a list in either case.""" + was_list = False + if not isinstance(y0, list): + y0 = [y0] + else: + was_list = True + + return y0, was_list + + +def _signals_to_list(signals): + """Check if signals is validly specified as a single signal specification or a list of + such specifications, and return as a list in either case.""" + was_list = False + if signals is None: + signals = [signals] + elif isinstance(signals, tuple): + # single Lindblad + signals = [signals] + elif isinstance(signals, list) and isinstance(signals[0], tuple): + # multiple lindblad + was_list = True + elif isinstance(signals, Schedule): + # pulse simulation + signals = [signals] + elif isinstance(signals, list) and isinstance(signals[0], Schedule): + # multiple pulse simulation + was_list = True + elif isinstance(signals, list) and isinstance(signals[0], (list, SignalList)): + # multiple Hamiltonian signals lists + was_list = True + elif isinstance(signals, SignalList) or ( + isinstance(signals, list) and not isinstance(signals[0], (list, SignalList)) + ): + # single Hamiltonian signals list + signals = [signals] + else: + raise QiskitError("Signals specified in invalid format.") + + return signals, was_list + + +def organize_signals_to_channels( + all_signals, all_channels, model_class, hamiltonian_channels, dissipator_channels +): + """Restructures a list of signals with order corresponding to all_channels, into the correctly + formatted data structure to pass into model.signals, according to the ordering specified + by hamiltonian_channels and dissipator_channels. + """ + + if model_class == HamiltonianModel: + if hamiltonian_channels is not None: + return [all_signals[all_channels.index(chan)] for chan in hamiltonian_channels] + else: + return None + else: + hamiltonian_signals = None + dissipator_signals = None + if hamiltonian_channels is not None: + hamiltonian_signals = [ + all_signals[all_channels.index(chan)] for chan in hamiltonian_channels + ] + if dissipator_channels is not None: + dissipator_signals = [ + all_signals[all_channels.index(chan)] for chan in dissipator_channels + ] + return (hamiltonian_signals, dissipator_signals) + + +def _nested_ndim(x): + """Determine the 'ndim' of x, which could be composed of nested lists and array types.""" + if isinstance(x, (list, tuple)): + return 1 + _nested_ndim(x[0]) + elif issubclass(type(x), Dispatch.REGISTERED_TYPES) or isinstance(x, Array): + return x.ndim + + # assume scalar + return 0 +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_modules/qiskit_dynamics/solvers/solver_functions.html b/stable/0.4/_modules/qiskit_dynamics/solvers/solver_functions.html new file mode 100644 index 000000000..1332a4a2d --- /dev/null +++ b/stable/0.4/_modules/qiskit_dynamics/solvers/solver_functions.html @@ -0,0 +1,791 @@ + + + + + + + + qiskit_dynamics.solvers.solver_functions - Qiskit Dynamics 0.4.5 documentation + + + + + + + + + + + + + + + + + + + + + Contents + + + + + + + + Menu + + + + + + + + + + + Expand + + + + + + + + + + + + + + + + Light mode + + + + + + + + + + + + + + Dark mode + + + + + + + Auto light/dark mode + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+ + + + + Back to top + +
+
+ +
+ +
+
+

Source code for qiskit_dynamics.solvers.solver_functions

+# -*- coding: utf-8 -*-
+
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2020.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+# pylint: disable=invalid-name,no-member,attribute-defined-outside-init
+
+r"""
+Solver functions.
+"""
+
+from typing import Optional, Union, Callable, Tuple, List, TypeVar
+from warnings import warn
+
+from scipy.integrate import OdeSolver
+
+from scipy.integrate._ivp.ivp import OdeResult  # pylint: disable=unused-import
+
+from qiskit import QiskitError
+from qiskit_dynamics.array import Array
+
+from qiskit_dynamics.models import (
+    BaseGeneratorModel,
+    GeneratorModel,
+    LindbladModel,
+)
+from qiskit_dynamics.models.hamiltonian_model import HamiltonianModel
+
+from .solver_utils import is_lindblad_model_not_vectorized
+from .fixed_step_solvers import (
+    RK4_solver,
+    jax_RK4_solver,
+    scipy_expm_solver,
+    lanczos_diag_solver,
+    jax_lanczos_diag_solver,
+    jax_expm_solver,
+    jax_RK4_parallel_solver,
+    jax_expm_parallel_solver,
+)
+from .scipy_solve_ivp import scipy_solve_ivp, SOLVE_IVP_METHODS
+from .jax_odeint import jax_odeint
+from .diffrax_solver import diffrax_solver
+
+ODE_METHODS = (
+    ["RK45", "RK23", "BDF", "DOP853", "Radau", "LSODA"]  # scipy solvers
+    + ["RK4"]  # fixed step solvers
+    + ["jax_odeint", "jax_RK4"]  # jax solvers
+)
+LMDE_METHODS = [
+    "scipy_expm",
+    "lanczos_diag",
+    "jax_lanczos_diag",
+    "jax_expm",
+    "jax_expm_parallel",
+    "jax_RK4_parallel",
+]
+
+# diffrax solver type placeholder
+DiffraxAbstractSolver = TypeVar("AbstractSolver")
+
+
+def _is_jax_method(method: any) -> bool:
+    """Check if method is a jax solver method."""
+    if method in [
+        "jax_odeint",
+        "jax_RK4",
+        "jax_expm",
+        "jax_expm_parallel",
+        "jax_RK4_parallel",
+        "jax_lanczos_diag",
+    ]:
+        return True
+
+    # only other jax methods are diffrax methods
+    return _is_diffrax_method(method)
+
+
+def _is_diffrax_method(method: any) -> bool:
+    """Check if method is a diffrax method."""
+    try:
+        from diffrax.solver import AbstractSolver
+
+        return isinstance(method, AbstractSolver)
+    except ImportError:
+        return False
+
+
+def _lanczos_validation(
+    rhs: Union[Callable, BaseGeneratorModel],
+    t_span: Array,
+    y0: Array,
+    k_dim: int,
+):
+    """Validation checks to run lanczos based solvers."""
+    if isinstance(rhs, BaseGeneratorModel):
+        if not isinstance(rhs, HamiltonianModel):
+            raise QiskitError(
+                """Lanczos solver can only be used for HamiltonianModel or function-based
+                    anti-Hermitian generators."""
+            )
+        if "sparse" not in rhs.evaluation_mode:
+            warn(
+                """lanczos_diag should be used with a generator in sparse mode
+                for better performance.""",
+                category=Warning,
+                stacklevel=2,
+            )
+
+    dim = rhs(t_span[0]).shape[0]
+    if k_dim > dim:
+        raise QiskitError("k_dim can be no larger than the dimension of the generator.")
+
+    if y0.ndim not in [1, 2]:
+        raise QiskitError("y0 must be 1d or 2d.")
+
+
+
+[docs] +def solve_ode( + rhs: Union[Callable, BaseGeneratorModel], + t_span: Array, + y0: Array, + method: Optional[Union[str, OdeSolver, DiffraxAbstractSolver]] = "DOP853", + t_eval: Optional[Union[Tuple, List, Array]] = None, + **kwargs, +): + r"""General interface for solving Ordinary Differential Equations (ODEs). + + ODEs are differential equations of the form + + .. math:: + + \dot{y}(t) = f(t, y(t)), + + where :math:`f` is a callable function and the state :math:`y(t)` is an + arbitrarily-shaped complex :class:`Array`. + + The ``method`` argument exposes a variety of underlying ODE solvers. Optional + arguments for any of the solver routines can be passed via ``kwargs``. + Available methods are: + + - ``scipy.integrate.solve_ivp`` - supports methods + ``['RK45', 'RK23', 'BDF', 'DOP853', 'Radau', 'LSODA']`` or by passing a valid + ``scipy`` :class:`OdeSolver` instance. + - ``'RK4'``: A fixed-step 4th order Runge-Kutta solver. + Requires additional kwarg ``max_dt``, indicating the maximum step + size to take. This solver will break integration periods into even + sub-intervals no larger than ``max_dt``, and step over each sub-interval + using the standard 4th order Runge-Kutta integration rule. + - ``'jax_RK4'``: JAX backend implementation of ``'RK4'`` method. + - ``'jax_odeint'``: Calls ``jax.experimental.ode.odeint`` variable step solver. + - ``diffrax.diffeqsolve`` - a JAX solver function, called by passing ``method`` + as a valid ``diffrax.solver.AbstractSolver`` instance. Requires the ``diffrax`` library. + + Results are returned as a :class:`OdeResult` object. + + Args: + rhs: RHS function :math:`f(t, y)`. + t_span: ``Tuple`` or ``list`` of initial and final time. + y0: State at initial time. + method: Solving method to use. + t_eval: Times at which to return the solution. Must lie within ``t_span``. If unspecified, + the solution will be returned at the points in ``t_span``. + **kwargs: Additional arguments to pass to the solver. + + Returns: + OdeResult: Results object. + + Raises: + QiskitError: If specified method does not exist. + """ + + if method not in ODE_METHODS and not ( + (isinstance(method, type) and (issubclass(method, OdeSolver))) or _is_diffrax_method(method) + ): + raise QiskitError("Method " + str(method) + " not supported by solve_ode.") + + y0 = Array(y0) + + if isinstance(rhs, BaseGeneratorModel): + _, solver_rhs, y0, model_in_frame_basis = setup_generator_model_rhs_y0_in_frame_basis( + rhs, y0 + ) + else: + solver_rhs = rhs + + # solve the problem using specified method + if method in SOLVE_IVP_METHODS or (isinstance(method, type) and issubclass(method, OdeSolver)): + results = scipy_solve_ivp(solver_rhs, t_span, y0, method, t_eval=t_eval, **kwargs) + elif isinstance(method, str) and method == "RK4": + results = RK4_solver(solver_rhs, t_span, y0, t_eval=t_eval, **kwargs) + elif isinstance(method, str) and method == "jax_RK4": + results = jax_RK4_solver(solver_rhs, t_span, y0, t_eval=t_eval, **kwargs) + elif isinstance(method, str) and method == "jax_odeint": + results = jax_odeint(solver_rhs, t_span, y0, t_eval=t_eval, **kwargs) + elif _is_diffrax_method(method): + results = diffrax_solver(solver_rhs, t_span, y0, method=method, t_eval=t_eval, **kwargs) + + # convert results out of frame basis if necessary + if isinstance(rhs, BaseGeneratorModel): + if not model_in_frame_basis: + results.y = results_y_out_of_frame_basis(rhs, Array(results.y), y0.ndim) + + # convert model back to original basis + rhs.in_frame_basis = model_in_frame_basis + + return results
+ + + +
+[docs] +def solve_lmde( + generator: Union[Callable, BaseGeneratorModel], + t_span: Array, + y0: Array, + method: Optional[Union[str, OdeSolver, DiffraxAbstractSolver]] = "DOP853", + t_eval: Optional[Union[Tuple, List, Array]] = None, + **kwargs, +): + r"""General interface for solving Linear Matrix Differential Equations (LMDEs) + in standard form. + + LMDEs in standard form are differential equations of the form: + + .. math:: + + \dot{y}(t) = G(t)y(t). + + where :math:`G(t)` is a square matrix valued-function called the *generator*, and :math:`y(t)` + is an :class:`Array` of appropriate shape. + + Thus function accepts :math:`G(t)` as a ``qiskit_dynamics`` model class, or as an arbitrary + callable. + + .. note:: + + Not all model classes are by-default in standard form. E.g. + :class:`~qiskit_dynamics.models.LindbladModel` represents an LMDE which is not typically + written in standard form. As such, using LMDE-specific methods with this generator requires + setting a vectorized evaluation mode. + + The ``method`` argument exposes solvers specialized to both LMDEs, as well as general ODE + solvers. If the method is not specific to LMDEs, the problem will be passed to + :meth:`~qiskit_dynamics.solve_ode` by automatically setting up the RHS function :math:`f(t, y) = + G(t)y`. + + Optional arguments for any of the solver routines can be passed via ``kwargs``. Available + LMDE-specific methods are: + + - ``'scipy_expm'``: A fixed-step matrix-exponential solver using ``scipy.linalg.expm``. Requires + additional kwarg ``max_dt`` indicating the maximum step size to take. This solver will break + integration periods into even sub-intervals no larger than ``max_dt`` and solve over each + sub-interval. The optional kwarg ``magnus_order`` controls the integration rule: if + ``magnus_order==1``, the generator is sampled at the interval midpoint and exponentiated, and + if ``magnus_order==2`` or ``magnus_order==3``, higher-order exponentiation rules are adopted + from :footcite:`blanes_magnus_2009`. The ``magnus_order`` parameter defaults to ``1``. + - ``'lanczos_diag'``: A fixed-step matrix-exponential solver, similar to ``'scipy_expm'`` but + restricted to anti-Hermitian generators. The matrix exponential is performed by diagonalizing + an approximate projection of the generator to a small subspace (the Krylov Subspace), obtained + via the Lanczos algorithm, and then exponentiating the eigenvalues. Requires additional kwargs + ``max_dt`` and ``k_dim`` indicating the maximum step size to take and Krylov subspace + dimension, respectively. ``k_dim`` acts as an adjustable accuracy parameter and can be no + larger than the dimension of the generator. The method is recommended for sparse systems with + large dimension. + - ``'jax_lanczos_diag'``: JAX implementation of ``'lanczos_diag'``, with the same arguments and + behaviour. Note that this method contains calls to ``jax.numpy.eigh``, which may have limited + validity when automatically differentiated. + - ``'jax_expm'``: JAX-implemented version of ``'scipy_expm'``, with the same arguments and + behaviour. Note that this method cannot be used for a model in sparse evaluation mode. + - ``'jax_expm_parallel'``: Same as ``'jax_expm'``, however all loops are implemented using + parallel operations. I.e. all matrix-exponentials for taking a single step are computed in + parallel using ``jax.vmap``, and are subsequently multiplied together in parallel using + ``jax.lax.associative_scan``. This method is only recommended for use with GPU execution. Note + that this method cannot be used for a model in sparse evaluation mode. + - ``'jax_RK4_parallel'``: 4th order Runge-Kutta fixed step solver. Under the assumption of the + structure of an LMDE, utilizes the same parallelization approach as ``'jax_expm_parallel'``, + however the single step rule is the standard 4th order Runge-Kutta rule, rather than + matrix-exponentiation. Requires and utilizes the ``max_dt`` kwarg in the same manner as + ``method='scipy_expm'``. This method is only recommended for use with GPU execution. + + Results are returned as a :class:`OdeResult` object. + + Args: + generator: Representation of generator function :math:`G(t)`. + t_span: ``Tuple`` or `list` of initial and final time. + y0: State at initial time. + method: Solving method to use. + t_eval: Times at which to return the solution. Must lie within ``t_span``. If unspecified, + the solution will be returned at the points in ``t_span``. + **kwargs: Additional arguments to pass to the solver. + + Returns: + OdeResult: Results object. + + Raises: + QiskitError: If specified method does not exist, + if dimension of ``y0`` is incompatible with generator dimension, or if an + LMDE-specific method is passed with a LindbladModel. + Additional Information: + While all :class:`~qiskit_dynamics.models.BaseGeneratorModel` subclasses represent LMDEs, + they are not all in standard form by defualt. Using an LMDE-specific models like + :class:`~qiskit_dynamics.models.LindbladModel` requires first setting a vectorized + evaluation mode. + """ + + # delegate to solve_ode if necessary + if ( + method in ODE_METHODS + or (isinstance(method, type) and (issubclass(method, OdeSolver))) + or _is_diffrax_method(method) + ): + if isinstance(generator, BaseGeneratorModel): + rhs = generator + else: + # treat generator as a function + def rhs(t, y): + return generator(t) @ y + + return solve_ode(rhs, t_span, y0, method=method, t_eval=t_eval, **kwargs) + + # raise error if neither an ODE_METHOD or an LMDE_METHOD + if method not in LMDE_METHODS: + raise QiskitError(f"Method {method} not supported by solve_lmde.") + + # lmde-specific methods can't be used with LindbladModel unless vectorized + if is_lindblad_model_not_vectorized(generator): + raise QiskitError( + """LMDE-specific methods with LindbladModel requires setting a + vectorized evaluation mode.""" + ) + + y0 = Array(y0) + + # setup generator and rhs functions to pass to numerical methods + if isinstance(generator, BaseGeneratorModel): + solver_generator, _, y0, model_in_frame_basis = setup_generator_model_rhs_y0_in_frame_basis( + generator, y0 + ) + else: + solver_generator = generator + + if method == "scipy_expm": + results = scipy_expm_solver(solver_generator, t_span, y0, t_eval=t_eval, **kwargs) + elif "lanczos_diag" in method: + _lanczos_validation(generator, t_span, y0, kwargs["k_dim"]) + if method == "lanczos_diag": + results = lanczos_diag_solver(solver_generator, t_span, y0, t_eval=t_eval, **kwargs) + elif method == "jax_lanczos_diag": + results = jax_lanczos_diag_solver(solver_generator, t_span, y0, t_eval=t_eval, **kwargs) + elif method == "jax_expm": + if isinstance(generator, BaseGeneratorModel) and "sparse" in generator.evaluation_mode: + raise QiskitError("jax_expm cannot be used with a generator in sparse mode.") + results = jax_expm_solver(solver_generator, t_span, y0, t_eval=t_eval, **kwargs) + elif method == "jax_expm_parallel": + results = jax_expm_parallel_solver(solver_generator, t_span, y0, t_eval=t_eval, **kwargs) + elif method == "jax_RK4_parallel": + results = jax_RK4_parallel_solver(solver_generator, t_span, y0, t_eval=t_eval, **kwargs) + + # convert results to correct basis if necessary + if isinstance(generator, BaseGeneratorModel): + if not model_in_frame_basis: + results.y = results_y_out_of_frame_basis(generator, Array(results.y), y0.ndim) + + generator.in_frame_basis = model_in_frame_basis + + return results
+ + + +def setup_generator_model_rhs_y0_in_frame_basis( + generator_model: BaseGeneratorModel, y0: Array +) -> Tuple[Callable, Callable, Array]: + """Helper function for setting up a subclass of + :class:`~qiskit_dynamics.models.BaseGeneratorModel` to be solved in the frame basis. + + Note: this function modifies ``generator_model`` to function in the frame basis. + + Args: + generator_model: Subclass of :class:`~qiskit_dynamics.models.BaseGeneratorModel`. + y0: Initial state. + + Returns: + Callable for generator in frame basis, callable for RHS in frame basis, y0 + in frame basis, and boolean indicating whether model was already specified in frame basis. + """ + + model_in_frame_basis = generator_model.in_frame_basis + + # if model not specified in frame basis, transform initial state into frame basis + if not model_in_frame_basis: + if ( + isinstance(generator_model, LindbladModel) + and "vectorized" in generator_model.evaluation_mode + ): + if generator_model.rotating_frame.frame_basis is not None: + y0 = generator_model.rotating_frame.vectorized_frame_basis_adjoint @ y0 + elif isinstance(generator_model, LindbladModel): + y0 = generator_model.rotating_frame.operator_into_frame_basis(y0) + elif isinstance(generator_model, GeneratorModel): + y0 = generator_model.rotating_frame.state_into_frame_basis(y0) + + # set model to operator in frame basis + generator_model.in_frame_basis = True + + # define rhs functions in frame basis + def generator(t): + return generator_model(t) + + def rhs(t, y): + return generator_model(t, y) + + return generator, rhs, y0, model_in_frame_basis + + +def results_y_out_of_frame_basis( + generator_model: BaseGeneratorModel, results_y: Array, y0_ndim: int +) -> Array: + """Convert the results of a simulation for :class:`~qiskit_dynamics.models.BaseGeneratorModel` + out of the frame basis. + + Args: + generator_model: Subclass of :class:`~qiskit_dynamics.models.BaseGeneratorModel`. + results_y: Array whose first index corresponds to the evaluation points of the state + for the results of ``solve_lmde`` or ``solve_ode``. + y0_ndim: Number of dimensions of initial state. + + Returns: + Callable for generator in frame basis, Callable for RHS in frame basis, and y0 + transformed to frame basis. + """ + # for left multiplication cases, if number of input dimensions is 1 + # vectorized basis transformation requires transposing before and after + if y0_ndim == 1: + results_y = results_y.T + + if ( + isinstance(generator_model, LindbladModel) + and "vectorized" in generator_model.evaluation_mode + ): + if generator_model.rotating_frame.frame_basis is not None: + results_y = generator_model.rotating_frame.vectorized_frame_basis @ results_y + elif isinstance(generator_model, LindbladModel): + results_y = generator_model.rotating_frame.operator_out_of_frame_basis(results_y) + else: + results_y = generator_model.rotating_frame.state_out_of_frame_basis(results_y) + + if y0_ndim == 1: + results_y = results_y.T + + return results_y +
+
+
+
+ +
+
+ + Made with Sphinx and @pradyunsg's + + Furo +
+ Last updated on 2024/03/19
+
+
+ +
+
+ +
+
+ +
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/stable/0.4/_sources/apidocs/array.rst.txt b/stable/0.4/_sources/apidocs/array.rst.txt new file mode 100644 index 000000000..ce8720f6c --- /dev/null +++ b/stable/0.4/_sources/apidocs/array.rst.txt @@ -0,0 +1,6 @@ +.. _qiskit_dynamics-array: + +.. automodule:: qiskit_dynamics.array + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/stable/0.4/_sources/apidocs/backend.rst.txt b/stable/0.4/_sources/apidocs/backend.rst.txt new file mode 100644 index 000000000..e69b81f46 --- /dev/null +++ b/stable/0.4/_sources/apidocs/backend.rst.txt @@ -0,0 +1,6 @@ +.. _qiskit_dynamics-backend: + +.. automodule:: qiskit_dynamics.backend + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/stable/0.4/_sources/apidocs/dispatch.rst.txt b/stable/0.4/_sources/apidocs/dispatch.rst.txt new file mode 100644 index 000000000..6b8e1e4cf --- /dev/null +++ b/stable/0.4/_sources/apidocs/dispatch.rst.txt @@ -0,0 +1,6 @@ +.. _qiskit_dynamics-dispatch: + +.. automodule:: qiskit_dynamics.dispatch + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/stable/0.4/_sources/apidocs/index.rst.txt b/stable/0.4/_sources/apidocs/index.rst.txt new file mode 100644 index 000000000..3d28fd3ac --- /dev/null +++ b/stable/0.4/_sources/apidocs/index.rst.txt @@ -0,0 +1,19 @@ +.. _qiskit-dynamics: + +.. module:: qiskit_dynamics + +============================= +Qiskit Dynamics API Reference +============================= + +.. toctree:: + :maxdepth: 2 + + solvers + backend + models + signals + pulse + perturbation + array + dispatch diff --git a/stable/0.4/_sources/apidocs/models.rst.txt b/stable/0.4/_sources/apidocs/models.rst.txt new file mode 100644 index 000000000..89e780254 --- /dev/null +++ b/stable/0.4/_sources/apidocs/models.rst.txt @@ -0,0 +1,6 @@ +.. _qiskit_dynamics-models: + +.. automodule:: qiskit_dynamics.models + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/stable/0.4/_sources/apidocs/perturbation.rst.txt b/stable/0.4/_sources/apidocs/perturbation.rst.txt new file mode 100644 index 000000000..8e68731d1 --- /dev/null +++ b/stable/0.4/_sources/apidocs/perturbation.rst.txt @@ -0,0 +1,6 @@ +.. _qiskit_dynamics-perturbation: + +.. automodule:: qiskit_dynamics.perturbation + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/stable/0.4/_sources/apidocs/pulse.rst.txt b/stable/0.4/_sources/apidocs/pulse.rst.txt new file mode 100644 index 000000000..610e4810c --- /dev/null +++ b/stable/0.4/_sources/apidocs/pulse.rst.txt @@ -0,0 +1,6 @@ +.. _qiskit_dynamics-pulse: + +.. automodule:: qiskit_dynamics.pulse + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/stable/0.4/_sources/apidocs/signals.rst.txt b/stable/0.4/_sources/apidocs/signals.rst.txt new file mode 100644 index 000000000..4e63527ae --- /dev/null +++ b/stable/0.4/_sources/apidocs/signals.rst.txt @@ -0,0 +1,6 @@ +.. _qiskit_dynamics-signals: + +.. automodule:: qiskit_dynamics.signals + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/stable/0.4/_sources/apidocs/solvers.rst.txt b/stable/0.4/_sources/apidocs/solvers.rst.txt new file mode 100644 index 000000000..825ed8cc7 --- /dev/null +++ b/stable/0.4/_sources/apidocs/solvers.rst.txt @@ -0,0 +1,6 @@ +.. _qiskit_dynamics-solvers: + +.. automodule:: qiskit_dynamics.solvers + :no-members: + :no-inherited-members: + :no-special-members: diff --git a/stable/0.4/_sources/discussions/dyson_magnus.rst.txt b/stable/0.4/_sources/discussions/dyson_magnus.rst.txt new file mode 100644 index 000000000..dc04b4214 --- /dev/null +++ b/stable/0.4/_sources/discussions/dyson_magnus.rst.txt @@ -0,0 +1,214 @@ +.. _perturbation review: + +Time-dependent perturbation theory and multi-variable series expansions review +============================================================================== + +The :mod:`.perturbation` module contains functionality for +numerically computing perturbation theory expansions used in the study of the +dynamics of quantum systems. Following :footcite:`puzzuoli_sensitivity_2022`, +this discussion reviews key concepts required +to understand and utilize the module, including the Dyson series and Magnus expansion, +their generalization to a multi-variable setting, and the notation used to represent +multi-variable power series in terms of multisets. + + +The Dyson series and Magnus expansion +------------------------------------- + +The Dyson series :footcite:`dyson_radiation_1949` and Magnus expansion +:footcite:`magnus_exponential_1954,blanes_magnus_2009` +are time-dependent perturbation theory expansions for solutions of linear matrix differential +equations (LMDEs). For an LMDE + +.. math:: + + \dot{U}(t) = G(t)U(t) + +with :math:`U(0) = I` the identity matrix, the Dyson series directly expands the solution: + +.. math:: + + U(t) &= I + \sum_{k=1}^\infty D_k(t)\textnormal{, with} \\ + D_k(t) &= \int_0^t dt_1 \dots \int_0^{t_{k-1}} dt_k G(t_1) \dots G(t_k). + +The Magnus expansion alternatively seeks to construct a time-averaged generator, i.e. an operator +:math:`\Omega(t)` for which + +.. math:: + + U(t) = \exp(\Omega(t)). + +The Magnus expansion provides +a series expansion, which, under certain conditions :footcite:`blanes_magnus_2009`, +converges to :math:`\Omega(t)`: + +.. math:: + + \Omega(t) = \sum_{k=1}^\infty \Omega_k(t), + +where explicit expressions for the :math:`\Omega_k(t)` are given in the literature +:footcite:`blanes_magnus_2009`. + + +Generalizing to the multi-variable case +--------------------------------------- + +In applications, these expansions are often used in a *multi-variable* setting, in which +the generator :math:`G(t)` depends on several variables :math:`c_0, \dots, c_{r-1}`, +and the Dyson series or Magnus expansion are used to understand how the evolution changes +under perturbations to several of these parameters simultaneously. For working with +these expansions algorithmically it is necessary to formalize +the expression of these expansions in the multi-variable setting. + +Mathematically, we explicitly write the generator as a function of these variables +:math:`G(t, c_0, \dots, c_{r-1})`, and expand :math:`G` in a +multi-variable power series in the variables :math:`c_i`: + +.. math:: + + G(t, c_0, \dots, c_{r-1}) = + G_\emptyset(t) + + \sum_{k=1}^\infty \sum_{0 \leq i_1 \leq \dots \leq i_k \leq r-1} + c_{i_1} \dots c_{i_k} G_{i_1, \dots, i_k}(t). + +For physical applications we take the existence of such a power series for granted: +up to constant factors, the coefficients :math:`G_{i_1, \dots, i_k}(t)` are the partial +derivatives of :math:`G` with respect to the variables :math:`c_i`. Commonly, :math:`G` +depends linearly on the variables, e.g. when representing couplings between quantum systems. + +Before defining the multi-variable Dyson series and Magnus expansions, we transform +the generator into the *toggling frame* of :math:`G_\emptyset(t)` +:footcite:`evans_timedependent_1967,haeberlen_1968`. Denoting + +.. math:: + + V(t) = \mathcal{T}\exp\left(\int_{t_0}^t ds G_\emptyset(s)\right), + +the generator :math:`G` in the toggling frame of :math:`G_\emptyset(t)`, +the unperturbed generator, is given by: + +.. math:: + + \tilde{G}(t, c_0, \dots, c_{r-1}) &= + \sum_{k=1}^\infty \sum_{0 \leq i_1 \leq \dots \leq i_k \leq r-1} + c_{i_1} \dots c_{i_k} \tilde{G}_{i_1, \dots, i_k}(t)\textnormal{, with} \\ + \tilde{G}_{i_1, \dots, i_k}(t) &= V^{-1}(t) G_{i_1, \dots, i_k}(t)V(t) + +Denoting :math:`U(t, c_0, \dots, c_{r-1})` as the solution of the LMDE with +generator :math:`\tilde{G}`, note that + +.. math:: + + U(t, c_0, \dots, c_{r-1}) = + V(t)\mathcal{T}\exp\left(\int_{t_0}^t ds \tilde{G}(s, c_0, \dots, c_{r-1})\right), + +and hence solution for :math:`G` and :math:`\tilde{G}` are simply related by :math:`V(t)`. + +Using this, :footcite:`puzzuoli_sensitivity_2022` defines the multi-variable Dyson series +for the generator :math:`\tilde{G}(t, c_0, \dots, c_{r-1})` as: + +.. math:: + + U(t, c_0, \dots, c_{r-1}) = I + + \sum_{k=1}^\infty \sum_{0 \leq i_1 \leq \dots \leq i_k \leq r-1} + c_{i_1} \dots c_{i_k} \mathcal{D}_{i_1, \dots, i_k}(t), + +where the :math:`\mathcal{D}_{i_1, \dots, i_k}(t)` are defined implicitly by the above +equation, and are called the *multi-variable Dyson series terms*. Similarly the +multi-variable Magnus expansion for :math:`\tilde{G}` is given as: + +.. math:: + + \Omega(t, c_0, \dots, c_{r-1}) = + \sum_{k=1}^\infty \sum_{0 \leq i_1 \leq \dots \leq i_k \leq r-1} + c_{i_1} \dots c_{i_k} \mathcal{O}_{i_1, \dots, i_k}(t), + +with the :math:`\mathcal{O}_{i_1, \dots, i_k}(t)` again defined implicitly, and called the +*multi-variable Magnus expansion terms*. + + +Computing multi-variable Dyson series and Magnus expansion terms +---------------------------------------------------------------- + +Given a power series decomposition of the generator as above, +the function :func:`.solve_lmde_perturbation` computes, +in the toggling frame of the unperturbed generator, either multi-variable +Dyson series or Magnus expansion terms via the algorithms in +:footcite:`puzzuoli_sensitivity_2022`. It can also be used to compute Dyson-like terms via +the algorithm in :footcite:`haas_engineering_2019`. In the presentation here and elsewhere, +the expansions are phrased as infinite series, but of course in practice truncated +versions must be specified and computed. + +Utilizing this function, and working with the other objects in the module, requires +understanding the notation and data structures used to represent power series. + +.. _multiset power series: + +Multiset power series notation +------------------------------ + +Following :footcite:`puzzuoli_sensitivity_2022`, the :mod:`.perturbation` +module utilizes a *multiset* notation to more compactly represent and work with power series. + +Consider the power series expansion above for the generator :math:`G(t, c_0, \dots, c_{r-1})`. +Structurally, each term in the power series is labelled by the number of times each +variable :math:`c_0, \dots, c_{r-1}` appears in the product :math:`c_{i_1} \dots c_{i_k}`. +Equivalently, each term may be indexed by the number of times each variable label +:math:`0, \dots, r-1` appears. The data structure used to represent these labels in this +module is that of a *multiset*, i.e. a "set with repeated entries". Denoting multisets +with round brackets, e.g. :math:`I = (i_1, \dots, i_k)`, we define + +.. math:: + + c_I = c_{i_1} \times \dots \times c_{i_k}. + +and similarly denote :math:`G_I = G_{i_1, \dots, i_k}`. This notation is chosen due to +the simple relationship between algebraic operations and multiset operations. E.g., +for two multisets :math:`I, J`, it holds that: + +.. math:: + + c_{I + J} = c_I \times c_J, + +where :math:`I + J` denotes the multiset whose object counts are the sum of both :math:`I` and +:math:`J`. + +Some example usages of this notation are: + + - :math:`c_{(0, 1)} = c_0 c_1`, + - :math:`c_{(1, 1)} = c_1^2`, and + - :math:`c_{(1, 2, 2, 3)} = c_1 c_2^2 c_3`. + +Finally, we denote the set of multisets of size $k$ with elements in :math:`\{0, \dots, r-1\}` +as :math:`\mathcal{I}_k(r)`. Combining everything, the power series for :math:`G` may be +rewritten as: + +.. math:: + + G(t, c_0, \dots, c_{r-1}) = G_\emptyset(t) + + \sum_{k=1}^\infty \sum_{I \in \mathcal{I}_k(r)} c_I G_I(t). + +Similarly, the multi-variable Dyson series is written as: + +.. math:: + + U(t, c_0, \dots, c_{r-1}) = + I + \sum_{k=1}^\infty \sum_{I \in \mathcal{I}_k(r)} c_I \mathcal{D}_I(t), + +and the multi-variable Magnus expansion as: + +.. math:: + + \Omega(t, c_0, \dots, c_{r-1}) = + \sum_{k=1}^\infty \sum_{I \in \mathcal{I}_k(r)} c_I \mathcal{O}_I(t). + +In the module, multisets are represented using the ``Multiset`` object in the +`multiset package `_. Arguments to functions +which must specify a multiset or a list of multisets accept either ``Multiset`` instances +directly, or a valid argument to the constructor to ``Multiset``, with the restriction that +the multiset entries must be non-negative integers. + + + + +.. footbibliography:: diff --git a/stable/0.4/_sources/discussions/index.rst.txt b/stable/0.4/_sources/discussions/index.rst.txt new file mode 100644 index 000000000..cab84b9b9 --- /dev/null +++ b/stable/0.4/_sources/discussions/index.rst.txt @@ -0,0 +1,16 @@ +#################### +Dynamics Discussions +#################### + +Discussions about topics and concepts related to the package. + +.. toctree:: + :maxdepth: 1 + + dyson_magnus.rst + + +.. Hiding - Indices and tables + :ref:`genindex` + :ref:`modindex` + :ref:`search` diff --git a/stable/0.4/_sources/index.rst.txt b/stable/0.4/_sources/index.rst.txt new file mode 100644 index 000000000..b9cb37812 --- /dev/null +++ b/stable/0.4/_sources/index.rst.txt @@ -0,0 +1,38 @@ +############################# +Qiskit Dynamics documentation +############################# + +Qiskit Dynamics is an open-source project for building, transforming, and solving +models of quantum systems in Qiskit. + +The goal of Qiskit Dynamics is to provide access to different numerical +methods, and to automate common processes typically performed by hand, +e.g. entering rotating frames, or doing the rotating wave approximation. + +Qiskit Dynamics can be configured to use either +`NumPy `_ or `JAX `_ +as the backend for array operations. `NumPy `_ is the default, +and `JAX `_ is an optional dependency, which enables +just-in-time compilation, automatic differentiation, and GPU execution of Qiskit Dynamics code. + +.. warning:: + + This package is still in the early stages of development and it is very likely + that there will be breaking API changes in future releases. + If you encounter any bugs please open an issue on + `Github `_ + + +.. toctree:: + :maxdepth: 1 + + Tutorials + User Guide + API References + Discussions + Release Notes + +.. Hiding - Indices and tables + :ref:`genindex` + :ref:`modindex` + :ref:`search` diff --git a/stable/0.4/_sources/release_notes.rst.txt b/stable/0.4/_sources/release_notes.rst.txt new file mode 100644 index 000000000..7e2951be4 --- /dev/null +++ b/stable/0.4/_sources/release_notes.rst.txt @@ -0,0 +1 @@ +.. release-notes:: Release Notes \ No newline at end of file diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.array.Array.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.array.Array.rst.txt new file mode 100644 index 000000000..32f385906 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.array.Array.rst.txt @@ -0,0 +1,24 @@ +Array +===== + +.. currentmodule:: qiskit_dynamics.array + +.. autoclass:: Array + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: Array.available_backends + .. automethod:: Array.default_backend + .. automethod:: Array.set_default_backend + + + + .. rubric:: Attributes + + .. autoattribute:: backend + .. autoattribute:: data diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.array.wrap.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.array.wrap.rst.txt new file mode 100644 index 000000000..1c6997cf6 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.array.wrap.rst.txt @@ -0,0 +1,6 @@ +qiskit\_dynamics.array.wrap +=========================== + +.. currentmodule:: qiskit_dynamics.array + +.. autofunction:: wrap \ No newline at end of file diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.backend.DynamicsBackend.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.backend.DynamicsBackend.rst.txt new file mode 100644 index 000000000..9cffa12bc --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.backend.DynamicsBackend.rst.txt @@ -0,0 +1,48 @@ +DynamicsBackend +=============== + +.. currentmodule:: qiskit_dynamics.backend + +.. autoclass:: DynamicsBackend + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: DynamicsBackend.acquire_channel + .. automethod:: DynamicsBackend.configuration + .. automethod:: DynamicsBackend.control_channel + .. automethod:: DynamicsBackend.defaults + .. automethod:: DynamicsBackend.drive_channel + .. automethod:: DynamicsBackend.from_backend + .. automethod:: DynamicsBackend.measure_channel + .. automethod:: DynamicsBackend.qubit_properties + .. automethod:: DynamicsBackend.run + .. automethod:: DynamicsBackend.set_options + + + + .. rubric:: Attributes + + .. autoattribute:: coupling_map + .. autoattribute:: dt + .. autoattribute:: dtm + .. autoattribute:: instruction_durations + .. autoattribute:: instruction_schedule_map + .. autoattribute:: instructions + .. autoattribute:: max_circuits + .. autoattribute:: meas_map + .. autoattribute:: num_qubits + .. autoattribute:: operation_names + .. autoattribute:: operations + .. autoattribute:: options + .. autoattribute:: provider + .. autoattribute:: target + .. autoattribute:: version + .. autoattribute:: name + .. autoattribute:: description + .. autoattribute:: online_date + .. autoattribute:: backend_version diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.backend.default_experiment_result_function.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.backend.default_experiment_result_function.rst.txt new file mode 100644 index 000000000..50ee9984d --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.backend.default_experiment_result_function.rst.txt @@ -0,0 +1,6 @@ +qiskit\_dynamics.backend.default\_experiment\_result\_function +============================================================== + +.. currentmodule:: qiskit_dynamics.backend + +.. autofunction:: default_experiment_result_function \ No newline at end of file diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.backend.parse_backend_hamiltonian_dict.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.backend.parse_backend_hamiltonian_dict.rst.txt new file mode 100644 index 000000000..b610dc14a --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.backend.parse_backend_hamiltonian_dict.rst.txt @@ -0,0 +1,6 @@ +qiskit\_dynamics.backend.parse\_backend\_hamiltonian\_dict +========================================================== + +.. currentmodule:: qiskit_dynamics.backend + +.. autofunction:: parse_backend_hamiltonian_dict \ No newline at end of file diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.dispatch.asarray.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.dispatch.asarray.rst.txt new file mode 100644 index 000000000..00335bdb2 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.dispatch.asarray.rst.txt @@ -0,0 +1,6 @@ +qiskit\_dynamics.dispatch.asarray +================================= + +.. currentmodule:: qiskit_dynamics.dispatch + +.. autofunction:: asarray \ No newline at end of file diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.dispatch.requires_backend.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.dispatch.requires_backend.rst.txt new file mode 100644 index 000000000..626835186 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.dispatch.requires_backend.rst.txt @@ -0,0 +1,6 @@ +qiskit\_dynamics.dispatch.requires\_backend +=========================================== + +.. currentmodule:: qiskit_dynamics.dispatch + +.. autofunction:: requires_backend \ No newline at end of file diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.models.GeneratorModel.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.models.GeneratorModel.rst.txt new file mode 100644 index 000000000..21963c0c2 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.models.GeneratorModel.rst.txt @@ -0,0 +1,29 @@ +GeneratorModel +============== + +.. currentmodule:: qiskit_dynamics.models + +.. autoclass:: GeneratorModel + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: GeneratorModel.copy + .. automethod:: GeneratorModel.evaluate + .. automethod:: GeneratorModel.evaluate_rhs + + + + .. rubric:: Attributes + + .. autoattribute:: dim + .. autoattribute:: evaluation_mode + .. autoattribute:: in_frame_basis + .. autoattribute:: operators + .. autoattribute:: rotating_frame + .. autoattribute:: signals + .. autoattribute:: static_operator diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.models.HamiltonianModel.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.models.HamiltonianModel.rst.txt new file mode 100644 index 000000000..4af473eeb --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.models.HamiltonianModel.rst.txt @@ -0,0 +1,29 @@ +HamiltonianModel +================ + +.. currentmodule:: qiskit_dynamics.models + +.. autoclass:: HamiltonianModel + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: HamiltonianModel.copy + .. automethod:: HamiltonianModel.evaluate + .. automethod:: HamiltonianModel.evaluate_rhs + + + + .. rubric:: Attributes + + .. autoattribute:: dim + .. autoattribute:: evaluation_mode + .. autoattribute:: in_frame_basis + .. autoattribute:: operators + .. autoattribute:: rotating_frame + .. autoattribute:: signals + .. autoattribute:: static_operator diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.models.LindbladModel.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.models.LindbladModel.rst.txt new file mode 100644 index 000000000..e066e3715 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.models.LindbladModel.rst.txt @@ -0,0 +1,33 @@ +LindbladModel +============= + +.. currentmodule:: qiskit_dynamics.models + +.. autoclass:: LindbladModel + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: LindbladModel.copy + .. automethod:: LindbladModel.evaluate + .. automethod:: LindbladModel.evaluate_hamiltonian + .. automethod:: LindbladModel.evaluate_rhs + .. automethod:: LindbladModel.from_hamiltonian + + + + .. rubric:: Attributes + + .. autoattribute:: dim + .. autoattribute:: dissipator_operators + .. autoattribute:: evaluation_mode + .. autoattribute:: hamiltonian_operators + .. autoattribute:: in_frame_basis + .. autoattribute:: rotating_frame + .. autoattribute:: signals + .. autoattribute:: static_dissipators + .. autoattribute:: static_hamiltonian diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.models.RotatingFrame.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.models.RotatingFrame.rst.txt new file mode 100644 index 000000000..fb5c77ac6 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.models.RotatingFrame.rst.txt @@ -0,0 +1,37 @@ +RotatingFrame +============= + +.. currentmodule:: qiskit_dynamics.models + +.. autoclass:: RotatingFrame + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: RotatingFrame.generator_into_frame + .. automethod:: RotatingFrame.generator_out_of_frame + .. automethod:: RotatingFrame.operator_into_frame + .. automethod:: RotatingFrame.operator_into_frame_basis + .. automethod:: RotatingFrame.operator_out_of_frame + .. automethod:: RotatingFrame.operator_out_of_frame_basis + .. automethod:: RotatingFrame.state_into_frame + .. automethod:: RotatingFrame.state_into_frame_basis + .. automethod:: RotatingFrame.state_out_of_frame + .. automethod:: RotatingFrame.state_out_of_frame_basis + .. automethod:: RotatingFrame.vectorized_map_into_frame + + + + .. rubric:: Attributes + + .. autoattribute:: dim + .. autoattribute:: frame_basis + .. autoattribute:: frame_basis_adjoint + .. autoattribute:: frame_diag + .. autoattribute:: frame_operator + .. autoattribute:: vectorized_frame_basis + .. autoattribute:: vectorized_frame_basis_adjoint diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.models.rotating_wave_approximation.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.models.rotating_wave_approximation.rst.txt new file mode 100644 index 000000000..db16e1e14 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.models.rotating_wave_approximation.rst.txt @@ -0,0 +1,6 @@ +qiskit\_dynamics.models.rotating\_wave\_approximation +===================================================== + +.. currentmodule:: qiskit_dynamics.models + +.. autofunction:: rotating_wave_approximation \ No newline at end of file diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.ArrayPolynomial.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.ArrayPolynomial.rst.txt new file mode 100644 index 000000000..f642c82b3 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.ArrayPolynomial.rst.txt @@ -0,0 +1,33 @@ +ArrayPolynomial +=============== + +.. currentmodule:: qiskit_dynamics.perturbation + +.. autoclass:: ArrayPolynomial + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: ArrayPolynomial.add + .. automethod:: ArrayPolynomial.compute_monomials + .. automethod:: ArrayPolynomial.conj + .. automethod:: ArrayPolynomial.matmul + .. automethod:: ArrayPolynomial.mul + .. automethod:: ArrayPolynomial.sum + .. automethod:: ArrayPolynomial.trace + .. automethod:: ArrayPolynomial.transpose + + + + .. rubric:: Attributes + + .. autoattribute:: array_coefficients + .. autoattribute:: constant_term + .. autoattribute:: monomial_labels + .. autoattribute:: ndim + .. autoattribute:: real + .. autoattribute:: shape diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.DysonLikeData.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.DysonLikeData.rst.txt new file mode 100644 index 000000000..9d3cb1839 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.DysonLikeData.rst.txt @@ -0,0 +1,23 @@ +DysonLikeData +============= + +.. currentmodule:: qiskit_dynamics.perturbation + +.. autoclass:: DysonLikeData + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: DysonLikeData.get_item + + + + .. rubric:: Attributes + + .. autoattribute:: metadata + .. autoattribute:: data + .. autoattribute:: labels diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.PowerSeriesData.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.PowerSeriesData.rst.txt new file mode 100644 index 000000000..e2cf91248 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.PowerSeriesData.rst.txt @@ -0,0 +1,23 @@ +PowerSeriesData +=============== + +.. currentmodule:: qiskit_dynamics.perturbation + +.. autoclass:: PowerSeriesData + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: PowerSeriesData.get_item + + + + .. rubric:: Attributes + + .. autoattribute:: metadata + .. autoattribute:: data + .. autoattribute:: labels diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.solve_lmde_perturbation.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.solve_lmde_perturbation.rst.txt new file mode 100644 index 000000000..74384835e --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.perturbation.solve_lmde_perturbation.rst.txt @@ -0,0 +1,6 @@ +qiskit\_dynamics.perturbation.solve\_lmde\_perturbation +======================================================= + +.. currentmodule:: qiskit_dynamics.perturbation + +.. autofunction:: solve_lmde_perturbation \ No newline at end of file diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.pulse.InstructionToSignals.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.pulse.InstructionToSignals.rst.txt new file mode 100644 index 000000000..5491826c4 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.pulse.InstructionToSignals.rst.txt @@ -0,0 +1,18 @@ +InstructionToSignals +==================== + +.. currentmodule:: qiskit_dynamics.pulse + +.. autoclass:: InstructionToSignals + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: InstructionToSignals.get_awg_signals + .. automethod:: InstructionToSignals.get_signals + + diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.signals.Convolution.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.Convolution.rst.txt new file mode 100644 index 000000000..a8b19d81c --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.Convolution.rst.txt @@ -0,0 +1,16 @@ +Convolution +=========== + +.. currentmodule:: qiskit_dynamics.signals + +.. autoclass:: Convolution + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + + + .. rubric:: Attributes + + .. autoattribute:: n_inputs diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.signals.DiscreteSignal.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.DiscreteSignal.rst.txt new file mode 100644 index 000000000..eaf05964b --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.DiscreteSignal.rst.txt @@ -0,0 +1,33 @@ +DiscreteSignal +============== + +.. currentmodule:: qiskit_dynamics.signals + +.. autoclass:: DiscreteSignal + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: DiscreteSignal.add_samples + .. automethod:: DiscreteSignal.complex_value + .. automethod:: DiscreteSignal.conjugate + .. automethod:: DiscreteSignal.draw + .. automethod:: DiscreteSignal.envelope + .. automethod:: DiscreteSignal.from_Signal + + + + .. rubric:: Attributes + + .. autoattribute:: carrier_freq + .. autoattribute:: dt + .. autoattribute:: duration + .. autoattribute:: is_constant + .. autoattribute:: name + .. autoattribute:: phase + .. autoattribute:: samples + .. autoattribute:: start_time diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.signals.DiscreteSignalSum.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.DiscreteSignalSum.rst.txt new file mode 100644 index 000000000..fd50a83f2 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.DiscreteSignalSum.rst.txt @@ -0,0 +1,36 @@ +DiscreteSignalSum +================= + +.. currentmodule:: qiskit_dynamics.signals + +.. autoclass:: DiscreteSignalSum + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: DiscreteSignalSum.add_samples + .. automethod:: DiscreteSignalSum.complex_value + .. automethod:: DiscreteSignalSum.conjugate + .. automethod:: DiscreteSignalSum.draw + .. automethod:: DiscreteSignalSum.envelope + .. automethod:: DiscreteSignalSum.flatten + .. automethod:: DiscreteSignalSum.from_Signal + .. automethod:: DiscreteSignalSum.from_SignalSum + + + + .. rubric:: Attributes + + .. autoattribute:: carrier_freq + .. autoattribute:: components + .. autoattribute:: dt + .. autoattribute:: duration + .. autoattribute:: is_constant + .. autoattribute:: name + .. autoattribute:: phase + .. autoattribute:: samples + .. autoattribute:: start_time diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.signals.IQMixer.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.IQMixer.rst.txt new file mode 100644 index 000000000..c34e86f6b --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.IQMixer.rst.txt @@ -0,0 +1,16 @@ +IQMixer +======= + +.. currentmodule:: qiskit_dynamics.signals + +.. autoclass:: IQMixer + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + + + .. rubric:: Attributes + + .. autoattribute:: n_inputs diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.signals.Signal.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.Signal.rst.txt new file mode 100644 index 000000000..13cde4098 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.Signal.rst.txt @@ -0,0 +1,27 @@ +Signal +====== + +.. currentmodule:: qiskit_dynamics.signals + +.. autoclass:: Signal + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: Signal.complex_value + .. automethod:: Signal.conjugate + .. automethod:: Signal.draw + .. automethod:: Signal.envelope + + + + .. rubric:: Attributes + + .. autoattribute:: carrier_freq + .. autoattribute:: is_constant + .. autoattribute:: name + .. autoattribute:: phase diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.signals.SignalList.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.SignalList.rst.txt new file mode 100644 index 000000000..2ffcc0eb6 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.SignalList.rst.txt @@ -0,0 +1,24 @@ +SignalList +========== + +.. currentmodule:: qiskit_dynamics.signals + +.. autoclass:: SignalList + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: SignalList.complex_value + .. automethod:: SignalList.conjugate + .. automethod:: SignalList.flatten + + + + .. rubric:: Attributes + + .. autoattribute:: components + .. autoattribute:: drift diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.signals.SignalSum.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.SignalSum.rst.txt new file mode 100644 index 000000000..5b1af525e --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.signals.SignalSum.rst.txt @@ -0,0 +1,29 @@ +SignalSum +========= + +.. currentmodule:: qiskit_dynamics.signals + +.. autoclass:: SignalSum + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: SignalSum.complex_value + .. automethod:: SignalSum.conjugate + .. automethod:: SignalSum.draw + .. automethod:: SignalSum.envelope + .. automethod:: SignalSum.flatten + + + + .. rubric:: Attributes + + .. autoattribute:: carrier_freq + .. autoattribute:: components + .. autoattribute:: is_constant + .. autoattribute:: name + .. autoattribute:: phase diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.DysonSolver.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.DysonSolver.rst.txt new file mode 100644 index 000000000..b2b47d70e --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.DysonSolver.rst.txt @@ -0,0 +1,21 @@ +DysonSolver +=========== + +.. currentmodule:: qiskit_dynamics.solvers + +.. autoclass:: DysonSolver + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: DysonSolver.solve + + + + .. rubric:: Attributes + + .. autoattribute:: model diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.MagnusSolver.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.MagnusSolver.rst.txt new file mode 100644 index 000000000..07338c02f --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.MagnusSolver.rst.txt @@ -0,0 +1,21 @@ +MagnusSolver +============ + +.. currentmodule:: qiskit_dynamics.solvers + +.. autoclass:: MagnusSolver + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: MagnusSolver.solve + + + + .. rubric:: Attributes + + .. autoattribute:: model diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.Solver.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.Solver.rst.txt new file mode 100644 index 000000000..30ff33f4a --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.Solver.rst.txt @@ -0,0 +1,21 @@ +Solver +====== + +.. currentmodule:: qiskit_dynamics.solvers + +.. autoclass:: Solver + :no-members: + :show-inheritance: + :no-inherited-members: + :no-special-members: + + .. rubric:: Methods + + + .. automethod:: Solver.solve + + + + .. rubric:: Attributes + + .. autoattribute:: model diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.solve_lmde.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.solve_lmde.rst.txt new file mode 100644 index 000000000..0314bcb27 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.solve_lmde.rst.txt @@ -0,0 +1,6 @@ +qiskit\_dynamics.solvers.solve\_lmde +==================================== + +.. currentmodule:: qiskit_dynamics.solvers + +.. autofunction:: solve_lmde \ No newline at end of file diff --git a/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.solve_ode.rst.txt b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.solve_ode.rst.txt new file mode 100644 index 000000000..28ed3e608 --- /dev/null +++ b/stable/0.4/_sources/stubs/qiskit_dynamics.solvers.solve_ode.rst.txt @@ -0,0 +1,6 @@ +qiskit\_dynamics.solvers.solve\_ode +=================================== + +.. currentmodule:: qiskit_dynamics.solvers + +.. autofunction:: solve_ode \ No newline at end of file diff --git a/stable/0.4/_sources/tutorials/Lindblad_dynamics_simulation.rst.txt b/stable/0.4/_sources/tutorials/Lindblad_dynamics_simulation.rst.txt new file mode 100644 index 000000000..1d17caf88 --- /dev/null +++ b/stable/0.4/_sources/tutorials/Lindblad_dynamics_simulation.rst.txt @@ -0,0 +1,275 @@ +Solving the Lindblad dynamics of a qubit chain +============================================== + +In this tutorial we walk through an example of solving the time +evolution of the density matrix of coupled qubits using +``qiskit_dynamics``. The model that we solve consists of a +time-independent Hamiltonian for qubits interacting with their nearest +neighbors along a ring. In addition, energy relaxation terms acting on +each qubit are modeled by using a non-Markovian master equation in +Lindblad form. We will discuss the system’s steady state as a function +of the model parameters. + +In the sections below we define a model, solve the dynamics and plot +some observables using the following steps: + +1. Define the number of qubits and precompute some matrix operators. +2. Define all relevant parameters and setup a :class:`Solver` instance with the model of the system, + consisting of the Hamiltonian and the jump operators of the Lindblad dissipator. +3. Define the initial state and other parameters for the initial value problem, + and evolve the system state. +4. Define observables and calculate their values as a function of time. +5. Plot some observables and discuss the results. + +The model that we solve describes the time evolution of :math:`N` qubits +with coherent dynamics dictated by a given Hamiltonian, and the +environment modeled as a memory-less (Markovian) bath. Together, the +density matrix evolves according to the Lindblad master equation, + +.. math:: \partial_t\rho = -\frac{i}{\hbar} \left[H,\rho\right] + \mathcal{D}[\rho]. + +The Hamiltonian is composed of a sum of single-qubit terms and two-qubit +interactions, + +.. math:: H = H_0 + H_I. + +We take the qubits to have identical parameters, with a diagonal term of +frequency :math:`\nu_z` and a transverse term of amplitude +:math:`\nu_x`. The single-qubit part of the Hamiltonian is + +.. math:: H_0 = \frac{1}{2}\sum_i^N \left[2 \pi \nu_z {Z}_i + 2 \pi \nu_x {X}_i\right], + +where :math:`\{X_i,Y_i,Z_i\}` are the Pauli matrices for qubit :math:`i` +(also written as :math:`\sigma_i^a` with :math:`a\in\{x,y,z\}`). + +We set the interactions between nearest-neighbor qubits to be of +“flip-flop” type, also called “XY” coupling. We consider periodic +boundary conditions, such that the last qubit is connected to the first +qubit as well, with + +.. math:: H_I = \frac{1}{2} \sum_{\langle i,j\rangle}2 \pi J \left[{X_i X_j} + {Y_i Y_j}\right] + +In ``qiskit``, each qubit’s ground state is by convention the state +:math:`|0\rangle` which is the eigenstate of :math:`Z` with eigenvalue 1 +(the state that is known also as “up”). Therefore the action of energy +relaxation terms describing damping into the environment tend to bring +qubits to this state, as generated by the Lindblad dissipator + +.. math:: \mathcal{D}[\rho] = \sum_i \Gamma\left(\sigma_i^+ \rho\sigma_i^- - \frac{1}{2} \{\sigma_i^- \sigma_i^+,\rho\}\right), + +with :math:`\sigma_i^{\pm}= \frac{1}{2}\left(X_i\pm i Y_i\right)`. + +1. Prepare the single-qubit operators +------------------------------------- + +In ``qiskit-dynamics``, dynamical simulations are defined using the +operators that act on the states appearing in the differential +equations. We start by creating the single-qubit operators that act on +each qubit in the simulation, and are represented as matrices in the +Hilbert space of :math:`N` qubits. Using ``qiskit`` library routines, it +is easy to create :math:`N`-qubit operators that are products of basic +single-qubit operators, by using labels (descriptive strings), and the +callable syntax of ``Pauli`` classes to indicate a qubit number, as +below. + +Below, we first set the number of qubits :math:`N` to be simulated, and +then prepare and store the single-qubit Pauli operators that will be +used in the rest of this tutorial. + +.. jupyter-execute:: + :hide-code: + + import warnings + warnings.filterwarnings('ignore', message='', category=Warning, module='', lineno=0, append=False) + + +.. jupyter-execute:: + + import numpy as np + from qiskit.quantum_info import Operator, Pauli + + N = 6 + + x_ops = [] + y_ops = [] + z_ops = [] + qubits = range(N) + zeros = Operator(np.zeros((2 ** N, 2 ** N))) + + for i in qubits: + X = zeros + Pauli('X')(i) + x_ops.append(X) + + Y = zeros + Pauli('Y')(i) + y_ops.append(Y) + + Z = zeros + Pauli('Z')(i) + z_ops.append(Z) + + +2. Setup the solver +------------------- + +In this section we setup a :class:`Solver` class that stores and manipulates +the model to be solved. In the following, we will set :math:`\hbar=1` +and set the driving amplitude to be :math:`\nu_x \equiv 1`. This sets the +time units, with the other frequency +parameters scaled accordingly. Below, we first set a few values for +these free parameters, and then create the Hamiltonian matrix and the +list of dissipator operators. We build the full Hamiltonian matrix by +summing all single-qubit and two-qubit terms. Since there are no +time-dependent terms, and we do not plan to take partial derivatives of +parameters, we do not use the :class:`Signal` class in this tutorial. See the other tutorials for various +generalizations of this approach supported with ``qiskit-dynamics``. + +.. jupyter-execute:: + + from qiskit_dynamics import Solver, Signal + + nu_z = 4. + nu_x = 1. + J = 4. + Gamma = 4. + + H = zeros + for i in qubits: + X = x_ops[i] + Z = z_ops[i] + H += .5 * 2 * np.pi * nu_x * X + H += .5 * 2 * np.pi * nu_z * Z + + if N > 1: + j = i + 1 if i < (N - 1) else 0 # Nearest neighbors, with periodic boundary conditions + op = zeros + Pauli('XX')(i, j) + H += .5 * 2 * np.pi * J * op + + op = zeros + Pauli('YY')(i, j) + H += .5 * 2 * np.pi * J * op + + L_ops = [] + L_sig = [] + for i in qubits: + X = x_ops[i] + Y = y_ops[i] + L_ops.append(np.sqrt(Gamma) * 0.5 * (X + 1j * Y)) + + solver = Solver(static_hamiltonian=H, static_dissipators=L_ops) + + +3. Define the simulation parameters and solve the dynamics +---------------------------------------------------------- + +We now define the initial state for the simulation, the time span to +simulate for, and the intermediate times for which the solution is +requested. + +.. jupyter-execute:: + + from qiskit.quantum_info import DensityMatrix + + t_final = 8. / Gamma + tau = .01 + + # A density matrix with all qubits in ground state + y0 = DensityMatrix.from_label('0' * N) + + n_steps = int(np.ceil(t_final / tau)) + 1 + t_eval = np.linspace(0., t_final, n_steps) + + sol = solver.solve(t_span=[0., t_final], y0=y0, t_eval=t_eval) + + +4. Define the observables and calculate their values +---------------------------------------------------- + +Below we calculate single-qubit Pauli expectation values for each qubit +as a function of time (which define also the Bloch vector), + +.. math:: \langle\sigma_i^a(t)\rangle, + +and also the mean components of the collective Bloch vector over all +qubits, at each evaluation time, + +.. math:: \frac{1}{N}\sum_i\langle\sigma_i^a(t)\rangle. + +Since both the model and the initial state as defined above are +translation invariant (all qubits have identical parameters, and there +is no boundary), we expect the solution to remain translation invariant +as well. Hence the mean Bloch vector should be equal to any qubit’s +Bloch vector, and observing that this equality holds is a simple and +useful verification of the numerical solution that will be added in the +next section. + +.. jupyter-execute:: + + n_times = len(sol.y) + x_data = np.zeros((N, n_times)) + y_data = np.zeros((N, n_times)) + z_data = np.zeros((N, n_times)) + x_mean = np.zeros((n_times,)) + y_mean = np.zeros((n_times,)) + z_mean = np.zeros((n_times,)) + + for t_i, sol_t in enumerate(sol.y): + for qubit, obs in enumerate(x_ops): + x_data[qubit, t_i] = sol_t.expectation_value(obs).real + x_mean[t_i] = np.mean(x_data[:, t_i]) + + for qubit, obs in enumerate(y_ops): + y_data[qubit, t_i] = sol_t.expectation_value(obs).real + y_mean[t_i] = np.mean(y_data[:, t_i]) + + for qubit, obs in enumerate(z_ops): + z_data[qubit, t_i] = sol_t.expectation_value(obs).real + z_mean[t_i] = np.mean(z_data[:, t_i]) + + +5. Plot some observables and discuss the results +------------------------------------------------ + +Finally, let’s plot some of the results of our dynamical simulation, +using the single-qubit observables calculated as a function of time. We +plot both the time evolution of the collective Bloch vector, and the +Bloch vector at the final time, depicted in 3D within the Bloch sphere. +We also print a warning if the Bloch vector at the final time is not +translation invariant according to a simplified random check of two +values (taken up to a small numerical precision). + +Looking at the figures below, we see that for the above parameters the +steady state is nearly pure, with a large ground state component and a +small tilt along the negative :math:`x` axis. The direction and +magnitude of the collective Bloch vector is determined by a nontrivial +competition between the single-site terms, the qubit interactions, and +the damping. To test this statement, if you go back and vary the +interaction strength :math:`J`, you can see that the steady state may +change significantly. For example for :math:`J=1` the collective Bloch +vector will significantly tilt along :math:`+x`, while for :math:`J=3` it +will significantly shorten (the steady state becomes a mixed state), +becoming tilted along :math:`-y`. This complex dependence of the +Bloch vector on the parameters can be systematically analyzed - we encourage you to try +it! + +.. jupyter-execute:: + + from qiskit.visualization import plot_bloch_vector + import matplotlib.pyplot as plt + %matplotlib inline + + fontsize = 16 + + _, ax = plt.subplots(figsize = (10, 6)) + plt.rcParams.update({'font.size': fontsize}) + plt.plot(t_eval, x_mean, label = '$ N^{-1}\sum_i \\langle X_i \\rangle$') + plt.plot(t_eval, y_mean, label = '$ N^{-1}\sum_i \\langle Y_i \\rangle$') + plt.plot(t_eval, z_mean, label = '$ N^{-1}\sum_i \\langle Z_i \\rangle$') + plt.legend(fontsize = fontsize) + ax.set_xlabel('$t$', fontsize = fontsize) + ax.set_title('Mean Bloch vector vs. $t$', fontsize = fontsize) + + display(plot_bloch_vector([x_mean[-1], y_mean[-1], z_mean[-1]], + f'Mean Bloch vector at $t = {t_eval[-1]}$')) + + if N > 1 and ((abs(x_mean[-1]) > 1e-5 and abs(x_data[0, -1] / x_mean[-1] - 1) > 1e-5 or + (abs(z_mean[-1]) > 1e-5 and abs(z_data[1, -1] / z_mean[-1] - 1) > 1e-5))): + print("The solution at the final time appears to break translation invariance. " + "The precision of the simulation should be examined.") diff --git a/stable/0.4/_sources/tutorials/Rabi_oscillations.rst.txt b/stable/0.4/_sources/tutorials/Rabi_oscillations.rst.txt new file mode 100644 index 000000000..6346b444e --- /dev/null +++ b/stable/0.4/_sources/tutorials/Rabi_oscillations.rst.txt @@ -0,0 +1,205 @@ +Simulating Rabi oscillations with noise and decoherence +======================================================= + +In this tutorial we walk through an example of solving using +``qiskit_dynamics`` the time evolution of a qubit being driven close to +resonance. The model that we solve consists of a single qubit perturbed +by a sinusoidal drive. In addition, will consider energy relaxation and +decoherence terms modeled by using a Lindblad master equation. + +In the sections below we define a model, solve the dynamics and plot the +qubit oscillations using the following steps: + +1. Setup a :class:`.Solver` with the Hamiltonian model +2. Define the initial state and simulation times, and evolve the system state. +3. Plot the qubit state as a function of time and discuss the results. +4. Solve again the the model with jump operators for the Lindblad dissipator, and plot the results. + +In the first step below, we model the time evolution of a qubit’s state +taken as a two-level system, using the Schrödinger equation with a +Hamiltonian containing a diagonal term of frequency :math:`\nu_z` and a +transverse term of amplitude :math:`\nu_x` and harmonic driving +frequency :math:`\nu_d` (see how the Hamiltonians are derived on +`Qiskit Textbook page on Introduction to Transmon Physics +`_), + +.. math:: H = \frac{1}{2} \times 2 \pi \nu_z {Z} + 2 \pi \nu_x \cos(2 \pi \nu_d t){X}, + +where :math:`\{X,Y,Z\}` are the Pauli matrices (also written as +:math:`\sigma^a` with :math:`a\in\{x,y,z\}`). + +1. Setup the solver with the Hamiltonian model +---------------------------------------------- + +In the following, we will set :math:`\hbar=1` and fix some arbitrary +time units, with all frequency parameters scaled accordingly. Below, we +first set a few values for these frequency parameters, and then setup the +:class:`.Solver` class instance that stores and manipulates the model to be +solved, using matrices and :class:`.Signal` instances. For the +time-independent :math:`z` term we set the signal to a constant, while +for the trasverse driving term we setup a harmonic signal. + +.. jupyter-execute:: + :hide-code: + + import warnings + warnings.filterwarnings('ignore', message='', category=Warning, module='', lineno=0, append=False) + + +.. jupyter-execute:: + + import numpy as np + from qiskit.quantum_info import Operator + from qiskit_dynamics import Solver, Signal + + + nu_z = 10. + nu_x = 1. + nu_d = 9.98 # Almost on resonance with the Hamiltonian's energy levels difference, nu_z + + X = Operator.from_label('X') + Y = Operator.from_label('Y') + Z = Operator.from_label('Z') + s_p = 0.5 * (X + 1j * Y) + + solver = Solver( + static_hamiltonian=.5 * 2 * np.pi * nu_z * Z, + hamiltonian_operators=[2 * np.pi * nu_x * X], + ) + +2. Solve the system +------------------- + +We now define the initial state for the simulation, the time span to +simulate for, and the intermediate times for which the solution is +requested, and solve the evolution. + +.. jupyter-execute:: + + from qiskit.quantum_info.states import Statevector + from qiskit.quantum_info import DensityMatrix + + t_final = .5 / nu_x + tau = .005 + + y0 = Statevector([1., 0.]) + + n_steps = int(np.ceil(t_final / tau)) + 1 + t_eval = np.linspace(0., t_final, n_steps) + signals = [Signal(envelope=1., carrier_freq=nu_d)] + + sol = solver.solve(t_span=[0., t_final], y0=y0, signals=signals, t_eval=t_eval) + +3. Plot the qubit state +----------------------- + +Below we define a local function that calculates the qubit’s Pauli +expectation values as a function of time (which define also the Bloch +vector), + +.. math:: \langle X(t)\rangle, \langle Y(t)\rangle, \langle Z(t)\rangle. + +The same function plots both these three curves, and the Bloch vector at +the final time, depicted in 3D on the Bloch sphere. We will reuse this +function in the next section. + +We see that for the parameters we have defined, the qubit has completed +almost exactly a :math:`\pi`-rotation of the qubit Bloch vector about +the :math:`x` axis, from the ground to the excited state (with many +intermediate rotations of its transverse component, whose amplitude +increases and decreases). This mechanism of Rabi oscillations is the +basis for the single-qubit gates used to manipulate quantum devices - in +particular this is a realization of the :math:`X` gate. + +.. jupyter-execute:: + + from qiskit.visualization import plot_bloch_vector + import matplotlib.pyplot as plt + %matplotlib inline + + fontsize = 16 + + def plot_qubit_dynamics(sol, t_eval, X, Y, Z): + n_times = len(sol.y) + x_data = np.zeros((n_times,)) + y_data = np.zeros((n_times,)) + z_data = np.zeros((n_times,)) + + for t_i, sol_t in enumerate(sol.y): + x_data[t_i] = sol_t.expectation_value(X).real + y_data[t_i] = sol_t.expectation_value(Y).real + z_data[t_i] = sol_t.expectation_value(Z).real + + _, ax = plt.subplots(figsize = (10, 6)) + plt.rcParams.update({'font.size': fontsize}) + plt.plot(t_eval, x_data, label = '$\\langle X \\rangle$') + plt.plot(t_eval, y_data, label = '$\\langle Y \\rangle$') + plt.plot(t_eval, z_data, label = '$\\langle Z \\rangle$') + plt.legend(fontsize = fontsize) + ax.set_xlabel('$t$', fontsize = fontsize) + ax.set_title('Bloch vector vs. $t$', fontsize = fontsize) + plt.show() + + display(plot_bloch_vector([x_data[-1], y_data[-1], z_data[-1]], + f'Bloch vector at $t = {t_eval[-1]}$')) + + plot_qubit_dynamics(sol, t_eval, X, Y, Z) + +4. Redefine the model with damping and decoherence +-------------------------------------------------- + +Now we add to our simulation an environment modeled as a memory-less +(Markovian) bath, solving the Lindblad master equation with the same +Hamiltonian as before, but accounting also for energy relaxation and +decoherence terms. We simulate the dynamics to times longer than the +typical relaxation times :math:`T_1=1/\Gamma_1` and +:math:`T_{\phi}=1/\Gamma_2`. The qubit’s state has to be described using +a density matrix, now evolving according to the Lindblad master +equation, + +.. math:: \partial_t\rho = -\frac{i}{\hbar} \left[H,\rho\right] + \mathcal{D}[\rho]. + +We take the Lindblad dissipator to consist of two terms, + +.. math:: \mathcal{D}[\rho] = \mathcal{D}_1[\rho] + \mathcal{D}_2[\rho]. + +The action of energy relaxation terms describing damping into the +environment with rate :math:`\Gamma_1` are generated by + +.. math:: \mathcal{D}_1[\rho] = \Gamma_1\left(\sigma^+ \rho\sigma^- - \frac{1}{2} \{\sigma^- \sigma^+,\rho\}\right), + +with :math:`\sigma^{\pm}= \frac{1}{2}\left(X\pm i Y\right)`. + +The second dissipator describes (“pure”) dephasing with rate +:math:`\Gamma_2`, and reads + +.. math:: \mathcal{D}_2[\rho] = \Gamma_2\left(Z \rho Z - \rho\right). + +We use the function defined above for calculating the Bloch vector +components, which can be done since in ``qiskit`` and in +``qiskit-dynamics`` the syntax of many functions is identical for both +state vectors and density matrices. The shrinking of the qubit’s state +within the Bloch sphere due to the incoherent evolution can be clearly +seen in the plots below. + +.. jupyter-execute:: + + Gamma_1 = .8 + Gamma_2 = .2 + + t_final = 5.5 / max(Gamma_1, Gamma_2) + + y0 = DensityMatrix.from_label('0') + solver = Solver( + static_hamiltonian=.5 * 2 * np.pi * nu_z * Z, + hamiltonian_operators=[.5 * 2 * np.pi * nu_x * X], + static_dissipators=[np.sqrt(Gamma_1) * s_p, np.sqrt(Gamma_2) * Z] + ) + + n_steps = int(np.ceil(t_final / tau)) + 1 + t_eval = np.linspace(0., t_final, n_steps) + signals = [Signal(envelope=1., carrier_freq=nu_d)] + + sol = solver.solve(t_span=[0., t_final], y0=y0, signals=signals, t_eval=t_eval) + + plot_qubit_dynamics(sol, t_eval, X, Y, Z) diff --git a/stable/0.4/_sources/tutorials/dynamics_backend.rst.txt b/stable/0.4/_sources/tutorials/dynamics_backend.rst.txt new file mode 100644 index 000000000..f178c35c1 --- /dev/null +++ b/stable/0.4/_sources/tutorials/dynamics_backend.rst.txt @@ -0,0 +1,500 @@ +.. Substitution to reduce text length. +.. |CRHamitonian| replace:: :class:`~qiskit_experiments.library.characterization.CrossResonanceHamiltonian` + +.. _dynamics backend: + +Simulating backends at the pulse-level with :class:`.DynamicsBackend` +===================================================================== + +In this tutorial we walk through how to use the :class:`.DynamicsBackend` class as a Qiskit +Dynamics-backed, pulse-level simulator of a real backend. In particular, we demonstrate how to +configure a :class:`.DynamicsBackend` to simulate pulse schedules, circuits whose gates have pulse +definitions, and calibration and characterization experiments from Qiskit Experiments. + +The sections of this tutorial are as follows: + +1. Configure Dynamics to use JAX. +2. Instantiating a minimally-configured :class:`.DynamicsBackend` with a 2 qubit model. +3. Simulating pulse schedules on the :class:`.DynamicsBackend`. +4. Simulating circuits at the pulse level using the :class:`.DynamicsBackend`. +5. Simulating single-qubit calibration processes via Qiskit Experiments. +6. Simulating 2 qubit interaction characterization via the |CRHamitonian| experiment. + +1. Configure Dynamics to use JAX +-------------------------------- + +Note that the :class:`.DynamicsBackend` internally performs just-in-time compilation automatically +when configured to use JAX. See the :ref:`User Guide entry on using JAX with Dynamics ` for more information. + +.. jupyter-execute:: + :hide-code: + + import warnings + warnings.filterwarnings('ignore', message='', category=Warning, module='', lineno=0, append=False) + +.. jupyter-execute:: + + # Configure to use JAX internally + import jax + jax.config.update("jax_enable_x64", True) + jax.config.update("jax_platform_name", "cpu") + from qiskit_dynamics.array import Array + Array.set_default_backend("jax") + +2. Instantiating a minimally-configured :class:`.DynamicsBackend` with a 2 qubit model +-------------------------------------------------------------------------------------- + +To create the :class:`.DynamicsBackend`, first specify a :class:`.Solver` instance using the model +details. Note that the choice of model depends on the type of device you wish to simulate. Here, we +will use a :math:`2` qubit fixed-frequency transmon model with fixed coupling, with the following +Hamiltonian (see the `Qiskit Textbook page on Circuit Quantum Electrodynamics +`_ for details on how transmon +Hamiltonians are derived): + +.. math:: + + H(t) = 2 \pi \nu_0 &N_0 + \pi \alpha_0 N_0 (N_0 - I) + 2 \pi \nu_1 N_1 + + \pi \alpha_1 N_1(N_1 - I) + 2 \pi J (a_0 + a_0^\dagger)(a_1 + a_1^\dagger) \\ + & + 2 \pi r_0 s_0(t)(a_0 + a_0^\dagger) + 2 \pi r_1 s_1(t)(a_1 + a_1^\dagger), + +where + +- :math:`\nu_0` and :math:`\nu_1` are the qubit frequencies, +- :math:`\alpha_0` and :math:`\alpha_1` are the qubit anharmonicities, +- :math:`J` is the coupling strength, +- :math:`r_0` and :math:`r_1` are the Rabi strengths, and :math:`s_0(t)` and :math:`s_1(t)` are the + drive signals, +- :math:`a_j` and :math:`a_j^\dagger` are the lowering and raising operators for qubit :math:`j`, + and +- :math:`N_0` and :math:`N_1` are the number operators for qubits :math:`0` and :math:`1` + respectively. + +.. jupyter-execute:: + + import numpy as np + + dim = 3 + + v0 = 4.86e9 + anharm0 = -0.32e9 + r0 = 0.22e9 + + v1 = 4.97e9 + anharm1 = -0.32e9 + r1 = 0.26e9 + + J = 0.002e9 + + a = np.diag(np.sqrt(np.arange(1, dim)), 1) + adag = np.diag(np.sqrt(np.arange(1, dim)), -1) + N = np.diag(np.arange(dim)) + + ident = np.eye(dim, dtype=complex) + full_ident = np.eye(dim**2, dtype=complex) + + N0 = np.kron(ident, N) + N1 = np.kron(N, ident) + + a0 = np.kron(ident, a) + a1 = np.kron(a, ident) + + a0dag = np.kron(ident, adag) + a1dag = np.kron(adag, ident) + + + static_ham0 = 2 * np.pi * v0 * N0 + np.pi * anharm0 * N0 * (N0 - full_ident) + static_ham1 = 2 * np.pi * v1 * N1 + np.pi * anharm1 * N1 * (N1 - full_ident) + + static_ham_full = static_ham0 + static_ham1 + 2 * np.pi * J * ((a0 + a0dag) @ (a1 + a1dag)) + + drive_op0 = 2 * np.pi * r0 * (a0 + a0dag) + drive_op1 = 2 * np.pi * r1 * (a1 + a1dag) + +Construct the :class:`.Solver` using the model details, including parameters necessary for pulse +simulation. See the :class:`.Solver` documentation, as well as the :ref:`tutorial example ` for more details. Here, we choose to perform the simulation in the rotating frame of the +static Hamiltonian, which provides performance improvements (see the user guide entry on +:ref:`configuring simulations for performance `). Note that the measurement +outcomes of :meth:`.DynamicsBackend.run` are independent of the choice of rotating frame in the +:class:`.Solver`, and as such we are free to choose the rotating frame that provides the best +performance. + +.. jupyter-execute:: + + from qiskit_dynamics import Solver + + # build solver + dt = 1/4.5e9 + + solver = Solver( + static_hamiltonian=static_ham_full, + hamiltonian_operators=[drive_op0, drive_op1, drive_op0, drive_op1], + rotating_frame=static_ham_full, + hamiltonian_channels=["d0", "d1", "u0", "u1"], + channel_carrier_freqs={"d0": v0, "d1": v1, "u0": v1, "u1": v0}, + dt=dt, + ) + +Next, instantiate the :class:`.DynamicsBackend`. The ``solver`` is used for simulation, +``subsystem_dims`` indicates how the full system decomposes for measurement data computation, and +``solver_options`` are consistent options used by :meth:`.Solver.solve` when simulating the +differential equation. The full list of allowable ``solver_options`` are the arguments to +:func:`.solve_ode`. + +Note that, to enable the internal automatic jit-compilation, we choose a JAX integration method. +Furthermore, note that in the solver options we set the max step size to the pulse sample width +``dt`` via the ``"hmax"`` argument for the method ``"jax_odeint"``. This is important for preventing +variable step solvers from accidentally stepping over pulses in schedules with long idle times. + +.. jupyter-execute:: + + from qiskit_dynamics import DynamicsBackend + + # Consistent solver option to use throughout notebook + solver_options = {"method": "jax_odeint", "atol": 1e-6, "rtol": 1e-8, "hmax": dt} + + backend = DynamicsBackend( + solver=solver, + subsystem_dims=[dim, dim], # for computing measurement data + solver_options=solver_options, # to be used every time run is called + ) + +Alternatively to the above, the :meth:`.DynamicsBackend.from_backend` method can be used to build +the :class:`.DynamicsBackend` from an existing backend. The above model, which was built manually, +was taken from qubits :math:`0` and :math:`1` of ``almaden``. + +3. Simulating pulse schedules on the :class:`.DynamicsBackend` +-------------------------------------------------------------- + +With the above backend, we can already simulate a list of pulse schedules. The code below generates +a list of schedules specifying experiments on qubit :math:`0`. The schedule is chosen to demonstrate +that the usual instructions work on the :class:`.DynamicsBackend`. + +.. note:: + + In the following constructed schedule, measurement is performed with an + :class:`~qiskit.pulse.instructions.Acquire` instruction of duration ``1``. Measurements in + :class:`.DynamicsBackend` are computed projectively at the start time of the acquire + instructions, and the effects of measurement stimulus through + :class:`~qiskit.pulse.channels.MeasureChannel`\s are not simulated unless explicitly put into + the model by the user. As such, the lack of :class:`~qiskit.pulse.channels.MeasureChannel` + stimulus, and the duration of the :class:`~qiskit.pulse.instructions.Acquire` instruction has no + impact on the returned results. + + +.. jupyter-execute:: + + %%time + + from qiskit import pulse + + sigma = 128 + num_samples = 256 + + schedules = [] + + for amp in np.linspace(0., 1., 10): + gauss = pulse.library.Gaussian( + num_samples, amp, sigma, name="Parametric Gauss" + ) + + with pulse.build() as schedule: + with pulse.align_sequential(): + pulse.play(gauss, pulse.DriveChannel(0)) + pulse.shift_phase(0.5, pulse.DriveChannel(0)) + pulse.shift_frequency(0.1, pulse.DriveChannel(0)) + pulse.play(gauss, pulse.DriveChannel(0)) + pulse.acquire(duration=1, qubit_or_channel=0, register=pulse.MemorySlot(0)) + + schedules.append(schedule) + + job = backend.run(schedules, shots=100) + result = job.result() + +Visualize one of the schedules. + +.. jupyter-execute:: + + schedules[3].draw() + +Retrieve the counts for one of the experiments as would be done using the results object from a real +backend. + +.. jupyter-execute:: + + result.get_counts(3) + +4. Simulating circuits at the pulse level using the :class:`.DynamicsBackend` +----------------------------------------------------------------------------- + +For the :class:`.DynamicsBackend` to simulate a circuit, each circuit element must have a +corresponding pulse schedule. These schedules can either be specified in the gates themselves, by +attaching calibrations, or by adding instructions to the :class:`~qiskit.transpiler.Target` +contained in the :class:`.DynamicsBackend`. + +4.1 Simulating circuits with attached calibrations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Build a simple circuit. Here we build one consisting of a single Hadamard gate on qubit :math:`0`, +followed by measurement. + +.. jupyter-execute:: + + from qiskit import QuantumCircuit + + circ = QuantumCircuit(1, 1) + circ.h(0) + circ.measure([0], [0]) + + circ.draw("mpl") + +Next, attach a calibration for the Hadamard gate on qubit :math:`0` to the circuit. Note that here +we are only demonstrating the mechanics of adding a calibration; we have not attempted to calibrate +the schedule to implement the Hadamard gate with high fidelity. + +.. jupyter-execute:: + + with pulse.build() as h_q0: + pulse.play( + pulse.library.Gaussian(duration=256, amp=0.2, sigma=50, name="custom"), + pulse.DriveChannel(0) + ) + + circ.add_calibration("h", qubits=[0], schedule=h_q0) + +Call run on the circuit, and get counts as usual. + +.. jupyter-execute:: + + %time res = backend.run(circ).result() + + res.get_counts(0) + +4.2 Simulating circuits via gate definitions in the backend :class:`~qiskit.transpiler.Target` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Alternatively to the above work flow, add the above schedule as the pulse-level definition of the +Hadamard gate on qubit :math:`0` to `backend.target`, which impacts how jobs are transpiled for the +backend. See the :class:`~qiskit.transpiler.Target` class documentation for further information. + +.. jupyter-execute:: + + from qiskit.circuit.library import HGate + from qiskit.transpiler import InstructionProperties + + backend.target.add_instruction(HGate(), {(0,): InstructionProperties(calibration=h_q0)}) + +Rebuild the same circuit, however this time we do not need to add the calibration for the Hadamard +gate to the circuit object. + +.. jupyter-execute:: + + circ2 = QuantumCircuit(1, 1) + circ2.h(0) + circ2.measure([0], [0]) + + %time result = backend.run(circ2).result() + +.. jupyter-execute:: + + result.get_counts(0) + +5. Simulating single-qubit calibration processes via Qiskit Experiments +----------------------------------------------------------------------- + +Next, we perform rough calibrations for ``X`` and ``SX`` gates on both qubits modeled in the +:class:`.DynamicsBackend`, following the single-qubit calibration tutorial for Qiskit Experiments. + +5.1 Configure the :class:`~qiskit.transpiler.Target` to include single qubit instructions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To enable running of the single qubit experiments, we add the following to the ``target``: + +- Qubit frequency properties (needed by experiments like + :class:`~qiskit_experiments.library.calibration.rough_frequency.RoughFrequencyCal`). Note that + setting the qubit frequencies in the ``target`` does not impact the behaviour of the + :class:`.DynamicsBackend` itself. It is purely a data field that does not impact functionality. + Previously set frequency properties, such as ``channel_carrier_freqs`` in the :class:`.Solver`, + will remain unchanged. Here, we set the frequencies to the undressed frequencies in the model. +- ``X`` and ``SX`` gate instructions, which the transpiler needs to check are supported by the + backend. +- Add definitions of ``RZ`` gates as phase shifts. These instructions control the phase of the drive + channels, as well as any control channels acting on a given qubit. +- Add a ``CX`` gate between qubits :math:`(0, 1)` and :math:`(1, 0)`. While this tutorial will not + be utilizing it, this ensures that validation steps checking that the device is fully connected + will pass. + +.. jupyter-execute:: + + from qiskit.circuit.library import XGate, SXGate, RZGate, CXGate + from qiskit.circuit import Parameter + from qiskit.providers.backend import QubitProperties + + target = backend.target + + # qubit properties + target.qubit_properties = [QubitProperties(frequency=v0), QubitProperties(frequency=v1)] + + # add instructions + target.add_instruction(XGate(), properties={(0,): None, (1,): None}) + target.add_instruction(SXGate(), properties={(0,): None, (1,): None}) + + target.add_instruction(CXGate(), properties={(0, 1): None, (1, 0): None}) + + # Add RZ instruction as phase shift for drag cal + phi = Parameter("phi") + with pulse.build() as rz0: + pulse.shift_phase(phi, pulse.DriveChannel(0)) + pulse.shift_phase(phi, pulse.ControlChannel(1)) + + with pulse.build() as rz1: + pulse.shift_phase(phi, pulse.DriveChannel(1)) + pulse.shift_phase(phi, pulse.ControlChannel(0)) + + target.add_instruction( + RZGate(phi), + {(0,): InstructionProperties(calibration=rz0), (1,): InstructionProperties(calibration=rz1)} + ) + +5.2 Prepare :class:`~qiskit_experiments.calibration_management.calibrations.Calibrations` object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Next, prepare the :class:`~qiskit_experiments.calibration_management.calibrations.Calibrations` +object. Here we use the +:class:`~qiskit_experiments.calibration_management.basis_gate_library.FixedFrequencyTransmon` +template library to initialize our calibrations. + +.. jupyter-execute:: + + import pandas as pd + from qiskit_experiments.calibration_management.calibrations import Calibrations + from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmon + + cals = Calibrations(libraries=[FixedFrequencyTransmon(basis_gates=['x', 'sx'])]) + + pd.DataFrame(**cals.parameters_table(qubit_list=[0, ()])) + +5.3 Rough amplitude calibration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Next, run a rough amplitude calibration for ``X`` and ``SX`` gates for both qubits. First, build the +experiments. + +.. jupyter-execute:: + + from qiskit_experiments.library.calibration import RoughXSXAmplitudeCal + + # rabi experiments for qubit 0 + rabi0 = RoughXSXAmplitudeCal([0], cals, backend=backend, amplitudes=np.linspace(-0.2, 0.2, 27)) + + # rabi experiments for qubit 1 + rabi1 = RoughXSXAmplitudeCal([1], cals, backend=backend, amplitudes=np.linspace(-0.2, 0.2, 27)) + +Run the Rabi experiments. + +.. jupyter-execute:: + + %%time + rabi0_data = rabi0.run().block_for_results() + rabi1_data = rabi1.run().block_for_results() + +Plot the results. + +.. jupyter-execute:: + + rabi0_data.figure(0) + +.. jupyter-execute:: + + rabi1_data.figure(0) + +Observe the updated parameters for qubit 0. + +.. jupyter-execute:: + + pd.DataFrame(**cals.parameters_table(qubit_list=[0, ()], parameters="amp")) + +5.4 Rough Drag parameter calibration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Run rough Drag parameter calibration for the ``X`` and ``SX`` gates. This follows the same procedure +as above. + +.. jupyter-execute:: + + from qiskit_experiments.library.calibration import RoughDragCal + + cal_drag0 = RoughDragCal([0], cals, backend=backend, betas=np.linspace(-20, 20, 15)) + cal_drag1 = RoughDragCal([1], cals, backend=backend, betas=np.linspace(-20, 20, 15)) + + cal_drag0.set_experiment_options(reps=[3, 5, 7]) + cal_drag1.set_experiment_options(reps=[3, 5, 7]) + + cal_drag0.circuits()[5].draw(output="mpl") + +.. jupyter-execute:: + + %%time + drag0_data = cal_drag0.run().block_for_results() + drag1_data = cal_drag1.run().block_for_results() + +.. jupyter-execute:: + + drag0_data.figure(0) + + +.. jupyter-execute:: + + drag1_data.figure(0) + +The updated calibrations object: + +.. jupyter-execute:: + + pd.DataFrame(**cals.parameters_table(qubit_list=[0, ()], parameters="amp")) + +6. Simulating 2 qubit interaction characterization via the |CRHamitonian| experiment +------------------------------------------------------------------------------------ + +Finally, simulate the |CRHamitonian| characterization experiment. + +First, we further configure the backend to run this experiment. This requires defining the control +channel map, which is a dictionary mapping control-target qubit index pairs (given as a tuple) to +the control channel index used to drive the corresponding cross-resonance interaction. This is +required by the experiment to determine which channel to drive for each control-target pair. + +.. jupyter-execute:: + + # set the control channel map + backend.set_options(control_channel_map={(0, 1): 0, (1, 0): 1}) + +Build the characterization experiment object, and update gate definitions in ``target`` with the +values for the single qubit gates calibrated above. + +.. jupyter-execute:: + + from qiskit_experiments.library import CrossResonanceHamiltonian + + cr_ham_experiment = CrossResonanceHamiltonian( + physical_qubits=(0, 1), + durations=np.linspace(1e-7, 1e-6, 17), + backend=backend + ) + + backend.target.update_from_instruction_schedule_map(cals.get_inst_map()) + +.. jupyter-execute:: + + cr_ham_experiment.circuits()[10].draw("mpl") + +Run the simulation. + +.. jupyter-execute:: + + %time data_cr = cr_ham_experiment.run().block_for_results() + + +.. jupyter-execute:: + + data_cr.figure(0) diff --git a/stable/0.4/_sources/tutorials/index.rst.txt b/stable/0.4/_sources/tutorials/index.rst.txt new file mode 100644 index 000000000..6acaf69de --- /dev/null +++ b/stable/0.4/_sources/tutorials/index.rst.txt @@ -0,0 +1,19 @@ +################## +Dynamics Tutorials +################## + + +.. toctree:: + :maxdepth: 1 + + Rabi_oscillations.rst + Lindblad_dynamics_simulation.rst + qiskit_pulse.rst + optimizing_pulse_sequence.rst + dynamics_backend.rst + + +.. Hiding - Indices and tables + :ref:`genindex` + :ref:`modindex` + :ref:`search` diff --git a/stable/0.4/_sources/tutorials/optimizing_pulse_sequence.rst.txt b/stable/0.4/_sources/tutorials/optimizing_pulse_sequence.rst.txt new file mode 100644 index 000000000..bbc25951c --- /dev/null +++ b/stable/0.4/_sources/tutorials/optimizing_pulse_sequence.rst.txt @@ -0,0 +1,391 @@ +.. _optimization tutorial: + +Gradient optimization of a pulse sequence +========================================= + +Here, we walk through an example of optimizing a single-qubit gate using +``qiskit_dynamics``. This tutorial requires JAX - see the user guide +on :ref:`How-to use JAX with qiskit-dynamics `. + +We will optimize an :math:`X`-gate on a model of a qubit system using +the following steps: + +1. Configure ``qiskit-dynamics`` to work with the JAX backend. +2. Setup a :class:`.Solver` instance with the model of the system. +3. Define a pulse sequence parameterization to optimize over. +4. Define a gate fidelity function. +5. Define an objective function for optimization. +6. Use JAX to differentiate the objective, then do the gradient optimization. +7. Repeat the :math:`X`-gate optimization, alternatively using pulse schedules to specify the control sequence. + + +1. Configure to use JAX +----------------------- + +First, set JAX to operate in 64-bit mode, and set JAX as the default +backend using :class:`.Array` for performing array operations. +This is necessary to enable automatic differentiation of the Qiskit Dynamics code +in this tutorial. See the user guide entry on using JAX +for a more detailed explanation of why this step is necessary. + +.. jupyter-execute:: + + import jax + jax.config.update("jax_enable_x64", True) + + # tell JAX we are using CPU + jax.config.update('jax_platform_name', 'cpu') + + from qiskit_dynamics.array import Array + Array.set_default_backend('jax') + + +2. Setup the solver +------------------- + +Here we will setup a :class:`.Solver` with a simple model of a qubit. The +Hamiltonian is: + +.. math:: H(t) = 2 \pi \nu \frac{Z}{2} + 2 \pi r s(t) \frac{X}{2} + +In the above: + +- :math:`\nu` is the qubit frequency, +- :math:`r` is the drive strength, +- :math:`s(t)` is the drive signal which we will optimize, and +- :math:`X` and :math:`Z` are the Pauli X and Z operators. + +We will setup the problem to be in the rotating frame of the drift term. + +Also note: The :class:`.Solver` is initialized *without* signals, as we will +update these and optimize over this later. + +.. jupyter-execute:: + + import numpy as np + from qiskit.quantum_info import Operator + from qiskit_dynamics import Solver + + v = 5. + r = 0.02 + + static_hamiltonian = 2 * np.pi * v * Operator.from_label('Z') / 2 + drive_term = 2 * np.pi * r * Operator.from_label('X') / 2 + + ham_solver = Solver( + hamiltonian_operators=[drive_term], + static_hamiltonian=static_hamiltonian, + rotating_frame=static_hamiltonian, + ) + + +3. Define a pulse sequence parameterization to optimize over +------------------------------------------------------------ + +We will optimize over signals that are: + +- On resonance with piecewise constant envelopes, +- Envelopes bounded between :math:`[-1, 1]`, +- Envelopes are smooth, in the sense that the change between adjacent + samples is small, and +- Envelope starts and ends at :math:`0`. + +In setting up our parameterization, we need t keep in mind that we will +use the BFGS optimization routine, and hence: + +- Optimization parameters must be *unconstrained*. +- Parameterization must be JAX-differentiable. + +We implement a parameterization as follows: + +- Input: Array ``x`` of real values. +- “Normalize” ``x`` by applying a JAX-differentiable function from + :math:`\mathbb{R} \rightarrow [-1, 1]`. +- Pad the normalized ``x`` with a :math:`0.` to start. +- “Smoothen” the above via convolution. +- Construct the signal using the above as the samples for a + piecewise-constant envelope, with carrier frequency on resonance. + +We remark that there are many other parameterizations that may achieve +the same ends, and may have more efficient strategies for achieving a +value of :math:`0` at the beginning and end of the pulse. This is only +meant to demonstrate the need for such an approach, and one simple +example of one. + +.. jupyter-execute:: + + from qiskit_dynamics import DiscreteSignal + from qiskit_dynamics.array import Array + from qiskit_dynamics.signals import Convolution + + # define convolution filter + def gaus(t): + sigma = 15 + _dt = 0.1 + return 2.*_dt/np.sqrt(2.*np.pi*sigma**2)*np.exp(-t**2/(2*sigma**2)) + + convolution = Convolution(gaus) + + # define function mapping parameters to signals + def signal_mapping(params): + samples = Array(params) + + # map samples into [-1, 1] + bounded_samples = np.arctan(samples) / (np.pi / 2) + + # pad with 0 at beginning + padded_samples = np.append(Array([0], dtype=complex), bounded_samples) + + # apply filter + output_signal = convolution(DiscreteSignal(dt=1., samples=padded_samples)) + + # set carrier frequency to v + output_signal.carrier_freq = v + + return output_signal + +Observe, for example, the signal generated when all parameters are +:math:`10^8`: + +.. jupyter-execute:: + + signal = signal_mapping(np.ones(80) * 1e8) + signal.draw(t0=0., tf=signal.duration * signal.dt, n=1000, function='envelope') + + +4. Define gate fidelity +----------------------- + +We will optimize an :math:`X` gate, and define the fidelity of the unitary :math:`U` +implemented by the pulse via the standard fidelity measure: + +.. math:: f(U) = \frac{|\text{Tr}(XU)|^2}{4} + +.. jupyter-execute:: + + X_op = Array(Operator.from_label('X')) + + def fidelity(U): + U = Array(U) + + return np.abs(np.sum(X_op * U))**2 / 4. + +5. Define the objective function +-------------------------------- + +The function we want to optimize consists of: + +- Taking a list of input samples and applying the signal mapping. +- Simulating the Schrodinger equation over the length of the pulse + sequence. +- Computing and return the infidelity (we minimize :math:`1-f(U)`). + +.. jupyter-execute:: + + def objective(params): + + # apply signal mapping and set signals + signal = signal_mapping(params) + + # Simulate + results = ham_solver.solve( + y0=np.eye(2, dtype=complex), + t_span=[0, signal.duration * signal.dt], + signals=[signal], + method='jax_odeint', + atol=1e-8, + rtol=1e-8 + ) + U = results.y[-1] + + # compute and return infidelity + fid = fidelity(U) + return 1. - fid.data + +6. Perform JAX transformations and optimize +------------------------------------------- + +Finally, we gradient optimize the objective: + +- Use ``jax.value_and_grad`` to transform the objective into a function + that computes both the objective and the gradient. +- Use ``jax.jit`` to just-in-time compile the function into optimized + `XLA `__ code. For the initial cost of + performing the compilation, this speeds up each call of the function, + speeding up the optimization. +- Call ``scipy.optimize.minimize`` with the above, with + ``method='BFGS'`` and ``jac=True`` to indicate that the passed + objective also computes the gradient. + +.. jupyter-execute:: + + from jax import jit, value_and_grad + from scipy.optimize import minimize + + jit_grad_obj = jit(value_and_grad(objective)) + + initial_guess = np.random.rand(80) - 0.5 + + opt_results = minimize(fun=jit_grad_obj, x0=initial_guess, jac=True, method='BFGS') + print(opt_results.message) + print('Number of function evaluations: ' + str(opt_results.nfev)) + print('Function value: ' + str(opt_results.fun)) + + +The gate is optimized to an :math:`X` gate, with deviation within the +numerical accuracy of the solver. + +We can draw the optimized signal, which is retrieved by applying the +``signal_mapping`` to the optimized parameters. + +.. jupyter-execute:: + + opt_signal = signal_mapping(opt_results.x) + + opt_signal.draw( + t0=0, + tf=opt_signal.duration * opt_signal.dt, + n=1000, + function='envelope', + title='Optimized envelope' + ) + + +Summing the signal samples yields approximately :math:`\pm 50`, which is +equivalent to what one would expect based on a rotating wave +approximation analysis. + +.. jupyter-execute:: + + opt_signal.samples.sum() + + +7. Repeat the :math:`X`-gate optimization, alternatively using pulse schedules to specify the control sequence. +---------------------------------------------------------------------------------------------------------------- + +Here, we perform the optimization again, however now we specify the parameterized control sequence +to optimize as a pulse schedule. + +We construct a Gaussian square pulse as a :class:`~qiskit.pulse.library.ScalableSymbolicPulse` +instance, parameterized by ``sigma`` and ``width``. Although qiskit pulse provides a +:class:`~qiskit.pulse.library.GaussianSquare`, this class is not JAX compatible. See the user guide +entry on :ref:`JAX-compatible pulse schedules `. + +.. jupyter-execute:: + + import sympy as sym + from qiskit import pulse + + def lifted_gaussian( + t: sym.Symbol, + center, + t_zero, + sigma, + ) -> sym.Expr: + t_shifted = (t - center).expand() + t_offset = (t_zero - center).expand() + + gauss = sym.exp(-((t_shifted / sigma) ** 2) / 2) + offset = sym.exp(-((t_offset / sigma) ** 2) / 2) + + return (gauss - offset) / (1 - offset) + + def gaussian_square_generated_by_pulse(params): + + sigma, width = params + _t, _duration, _amp, _sigma, _width, _angle = sym.symbols( + "t, duration, amp, sigma, width, angle" + ) + _center = _duration / 2 + + _sq_t0 = _center - _width / 2 + _sq_t1 = _center + _width / 2 + + _gaussian_ledge = lifted_gaussian(_t, _sq_t0, -1, _sigma) + _gaussian_redge = lifted_gaussian(_t, _sq_t1, _duration + 1, _sigma) + + envelope_expr = ( + _amp + * sym.exp(sym.I * _angle) + * sym.Piecewise( + (_gaussian_ledge, _t <= _sq_t0), (_gaussian_redge, _t >= _sq_t1), (1, True) + ) + ) + + # we need to set disable_validation True to enable jax-jitting. + pulse.ScalableSymbolicPulse.disable_validation = True + + return pulse.ScalableSymbolicPulse( + pulse_type="GaussianSquare", + duration=230, + amp=1, + angle=0, + parameters={"sigma": sigma, "width": width}, + envelope=envelope_expr, + constraints=sym.And(_sigma > 0, _width >= 0, _duration >= _width), + valid_amp_conditions=sym.Abs(_amp) <= 1.0, + ) + +Next, we construct a pulse schedule using the above parametrized Gaussian square pulse, convert it +to a signal, and simulate the equation over the length of the pulse sequence. + +.. jupyter-execute:: + + from qiskit_dynamics.pulse import InstructionToSignals + + dt = 0.222 + w = 5. + + def objective(params): + + instance = gaussian_square_generated_by_pulse(params) + + with pulse.build() as Xp: + pulse.play(instance, pulse.DriveChannel(0)) + + converter = InstructionToSignals(dt, carriers={"d0": w}) + signal = converter.get_signals(Xp) + + result = ham_solver.solve( + y0=np.eye(2, dtype=complex), + t_span=[0, instance.duration * dt], + signals=[signal], + method='jax_odeint', + atol=1e-8, + rtol=1e-8 + ) + return 1. - fidelity(Array(result[0].y[-1])).data + + +We set the initial values of ``sigma`` and ``width`` for the optimization as +``initial_params = np.array([10, 10])``. + +.. jupyter-execute:: + + initial_params = np.array([10, 10]) + gaussian_square_generated_by_pulse(initial_params).draw() + +.. jupyter-execute:: + + from jax import jit, value_and_grad + from scipy.optimize import minimize + + jit_grad_obj = jit(value_and_grad(objective)) + + initial_params = np.array([10,10]) + + + opt_results = minimize(fun=jit_grad_obj, x0=initial_params, jac=True, method='BFGS') + + print(opt_results.message) + print(f"Optimized Sigma is {opt_results.x[0]} and Width is {opt_results.x[1]}") + print('Number of function evaluations: ' + str(opt_results.nfev)) + print('Function value: ' + str(opt_results.fun)) + + + +We can draw the optimized pulse, whose parameters are retrieved by ``opt_results.x``. + +.. jupyter-execute:: + + gaussian_square_generated_by_pulse(opt_results.x).draw() \ No newline at end of file diff --git a/stable/0.4/_sources/tutorials/qiskit_pulse.rst.txt b/stable/0.4/_sources/tutorials/qiskit_pulse.rst.txt new file mode 100644 index 000000000..2725c5a88 --- /dev/null +++ b/stable/0.4/_sources/tutorials/qiskit_pulse.rst.txt @@ -0,0 +1,175 @@ +Simulating Qiskit Pulse Schedules with Qiskit Dynamics +====================================================== + +This tutorial shows how to use Qiskit Dynamics to simulate a Pulse schedule +with a simple model of a qubit. The +qubit is modeled by the drift hamiltonian + +.. math:: + + H_\text{drift} = \frac{\omega}{2} Z + + +to which we apply the drive + +.. math:: + + H_\text{drive}(t) = \frac{r\,\Omega(t)}{2} X + +Here, :math:`\Omega(t)` is the drive signal which we will create using +Qiskit pulse. The factor :math:`r` is the strength with which the drive +signal drives the qubit. We begin by creating a pulse schedule with a +``sx`` gate followed by a phase shift on the drive so that the following +pulse creates a ``sy`` rotation. Therefore, if the qubit begins in the +ground state we expect that this second pulse will not have any effect +on the qubit. This situation is simulated with the following steps: + +1. Create the pulse schedule +2. Converting pulse schedules to a :class:`.Signal` +3. Create the system model, configured to simulate pulse schedules +4. Simulate the pulse schedule using the model + +1. Create the pulse schedule +---------------------------- + +First, we use the pulse module in Qiskit to create a pulse schedule. + +.. jupyter-execute:: + + import numpy as np + import qiskit.pulse as pulse + + # Strength of the Rabi-rate in GHz. + r = 0.1 + + # Frequency of the qubit transition in GHz. + w = 5. + + # Sample rate of the backend in ns. + dt = 1 / 4.5 + + # Define gaussian envelope function to approximately implement an sx gate. + amp = 1. / 1.75 + sig = 0.6985/r/amp + T = 4*sig + duration = int(T / dt) + beta = 2.0 + + with pulse.build(name="sx-sy schedule") as sxp: + pulse.play(pulse.Drag(duration, amp, sig / dt, beta), pulse.DriveChannel(0)) + pulse.shift_phase(np.pi/2, pulse.DriveChannel(0)) + pulse.play(pulse.Drag(duration, amp, sig / dt, beta), pulse.DriveChannel(0)) + + sxp.draw() + + +2. Convert the pulse schedule to a :class:`.Signal` +--------------------------------------------------- + +Qiskit Dynamics has functionality for converting pulse schedule to instances +of :class:`.Signal`. This is done using the pulse instruction to signal +converter :class:`.InstructionToSignals`. This converter needs to know the +sample rate of the arbitrary waveform generators creating the signals, +i.e. ``dt``, as well as the carrier frequency of the signals, +i.e. ``w``. The plot below shows the envelopes and the signals resulting +from this conversion. The dashed line shows the time at which the +virtual ``Z`` gate is applied. + +.. jupyter-execute:: + + from matplotlib import pyplot as plt + from qiskit_dynamics.pulse import InstructionToSignals + + plt.rcParams["font.size"] = 16 + + converter = InstructionToSignals(dt, carriers={"d0": w}) + + signals = converter.get_signals(sxp) + fig, axs = plt.subplots(1, 2, figsize=(14, 4.5)) + for ax, title in zip(axs, ["envelope", "signal"]): + signals[0].draw(0, 2*T, 2000, title, axis=ax) + ax.set_xlabel("Time (ns)") + ax.set_ylabel("Amplitude") + ax.set_title(title) + ax.vlines(T, ax.get_ylim()[0], ax.get_ylim()[1], "k", linestyle="dashed") + +.. _pulse solver: + +3. Create the system model +-------------------------- + +We now setup a :class:`.Solver` instance with the desired Hamiltonian information, +and configure it to simulate pulse schedules. This requires specifying +which channels act on which operators, channel carrier frequencies, and sample width ``dt``. +Additionally, we setup this solver in the rotating frame and perform the +rotating wave approximation. + +.. jupyter-execute:: + + from qiskit.quantum_info.operators import Operator + from qiskit_dynamics import Solver + + # construct operators + X = Operator.from_label('X') + Z = Operator.from_label('Z') + + drift = 2 * np.pi * w * Z/2 + operators = [2 * np.pi * r * X/2] + + # construct the solver + hamiltonian_solver = Solver( + static_hamiltonian=drift, + hamiltonian_operators=operators, + rotating_frame=drift, + rwa_cutoff_freq=2 * 5.0, + hamiltonian_channels=['d0'], + channel_carrier_freqs={'d0': w}, + dt=dt + ) + +4. Simulate the pulse schedule using the model +---------------------------------------------- + +In the last step we perform the simulation and plot the results. Note that, as we have +configured ``hamiltonian_solver`` to simulate pulse schedules, we pass the schedule ``xp`` +directly to the ``signals`` argument of the ``solve`` method. Equivalently, ``signals`` +generated by ``converter.get_signals`` above can also be passed to the ``signals`` argument +and in this case should produce identical behavior. + +.. jupyter-execute:: + + from qiskit.quantum_info.states import Statevector + + # Start the qubit in its ground state. + y0 = Statevector([1., 0.]) + + %time sol = hamiltonian_solver.solve(t_span=[0., 2*T], y0=y0, signals=sxp, atol=1e-8, rtol=1e-8) + + +.. jupyter-execute:: + + def plot_populations(sol): + pop0 = [psi.probabilities()[0] for psi in sol.y] + pop1 = [psi.probabilities()[1] for psi in sol.y] + + fig = plt.figure(figsize=(8, 5)) + plt.plot(sol.t, pop0, lw=3, label="Population in |0>") + plt.plot(sol.t, pop1, lw=3, label="Population in |1>") + plt.xlabel("Time (ns)") + plt.ylabel("Population") + plt.legend(frameon=False) + plt.ylim([0, 1.05]) + plt.xlim([0, 2*T]) + plt.vlines(T, 0, 1.05, "k", linestyle="dashed") + +The plot below shows the population of the qubit as it evolves during +the pulses. The vertical dashed line shows the time of the virtual Z +rotation which was induced by the ``shift_phase`` instruction in the +pulse schedule. As expected, the first pulse moves the qubit to an +eigenstate of the ``Y`` operator. Therefore, the second pulse, which +drives around the ``Y``-axis due to the phase shift, has hardley any +influence on the populations of the qubit. + +.. jupyter-execute:: + + plot_populations(sol) diff --git a/stable/0.4/_sources/userguide/how_to_configure_simulations.rst.txt b/stable/0.4/_sources/userguide/how_to_configure_simulations.rst.txt new file mode 100644 index 000000000..64b0babb6 --- /dev/null +++ b/stable/0.4/_sources/userguide/how_to_configure_simulations.rst.txt @@ -0,0 +1,332 @@ +.. _configuring simulations: + +How-to customize simulations using model transformations and evaluation modes +============================================================================= + +Qiskit Dynamics provides various options for configuring simulations +that can impact solver performance. These options include choosing between +dense and sparse array representations, different differential equation +solvers, and model transformations that modify the definition of the problem. +Depending on the specifics of the problem, different configurations can +yield better performance. + +Here we walk through some of these options, covering: + +1. How-to set up a simulation in a rotating frame, and its potential + benefits +2. How-to perform a rotating wave approximation, and its potential + benefits +3. How-to use a sparse evaluation mode, and how-to appropriately set a + rotating frame to preserve sparsity + + +Throughout this guide we work at the level of the :class:`.Solver` interface, +and consider Hamiltonian dynamics for simplicity, however all of the +considerations have their analogs for Lindblad dynamics. + +1. How-to set up a simulation in a rotating frame, and its potential benefits +----------------------------------------------------------------------------- + +Here we show how to perform a simulation in a rotating frame by setting the +optional ``rotating_frame`` argument when instantiating a :class:`.Solver`, and demonstrate how a +well-chosen frame operator :math:`F = -iH_0` can reduce solving time. +See the :ref:`Rotating frames section of the Models API documentation ` +for details on rotating frames. + +We will simulate the unitary generated by a transmon model with +Hamiltonian: + +.. math:: H(t) = 2 \pi \nu N + \pi \alpha N(N-I) + s(t) \times 2 \pi r (a + a^\dagger) + +where: + + - :math:`N`, :math:`a`, and :math:`a^\dagger` are, respectively, + the number, annihilation, and creation operators. + - :math:`\nu` is the qubit frequency and :math:`r` is the drive strength. + - :math:`s(t)` is the drive signal, which we will take to be on resonance with constant + envelope :math:`1`. + +First, construct the components of the model: + +.. jupyter-execute:: + + import numpy as np + from qiskit.quantum_info import Operator + from qiskit_dynamics import Solver, Signal + + dim = 5 + + v = 5. + anharm = -0.33 + r = 0.1 + + a = np.diag(np.sqrt(np.arange(1, dim)), 1) + adag = np.diag(np.sqrt(np.arange(1, dim)), -1) + N = np.diag(np.arange(dim)) + + # static part + static_hamiltonian = 2 * np.pi * v * N + np.pi * anharm * N * (N - np.eye(dim)) + # drive term + drive_hamiltonian = 2 * np.pi * r * (a + adag) + # drive signal + drive_signal = Signal(1., carrier_freq=v) + + # total simulation time + T = 1. / r + +Construct a :class:`.Solver` for the model as stated, without entering a rotating frame, and solve, +timing the solver. + +.. jupyter-execute:: + + solver = Solver( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=[drive_hamiltonian], + ) + + y0 = np.eye(dim, dtype=complex) + %time results = solver.solve(t_span=[0., T], y0=y0, signals=[drive_signal], atol=1e-10, rtol=1e-10) + +Next, define a :class:`.Solver` in the rotating frame of the static +Hamiltonian by setting the ``rotating_frame`` kwarg, and solve, again timing the solver. + +.. jupyter-execute:: + + rf_solver = Solver( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=[drive_hamiltonian], + rotating_frame=static_hamiltonian + ) + + y0 = np.eye(dim, dtype=complex) + %time rf_results = rf_solver.solve(t_span=[0., T], y0=y0, signals=[drive_signal], atol=1e-10, rtol=1e-10) + +Observe that despite the two simulation problems being mathematically equivalent, it takes +less time to solve in the rotating frame. + +Next, verify that the results are numerically equivalent. This requires +transforming the results to a common frame, which may be done via utility +functions in the ``RotatingFrame`` instance stored within ``Solver.model.rotating_frame``. + +To compare the results, we use the fidelity function for unitary matrices: + +.. math:: f(U, V) = \frac{|Tr(U^\dagger V)|^2}{d^2}, + +where :math:`d` is the dimension. A value of :math:`1` indicates equality of the unitaries. + +.. jupyter-execute:: + + def fidelity(U, V): + # the fidelity function + inner_product = (U.conj() * V).sum() + return (np.abs(inner_product) / dim) ** 2 + + U = results.y[-1] + # transform the results of the solver in the rotating frame out of the rotating frame + U_rf = rf_solver.model.rotating_frame.state_out_of_frame(T, rf_results.y[-1]) + + fidelity(U, U_rf) + +Based on the fidelity, we see that the two simulations are numerically equivalent +with reasonable accuracy based on our specified tolerances. + +The discrepancy in solving times can be understood by examining the number of +right-hand side (RHS) evaluations when solving the differential equation in each instance. +The number of RHS evaluations for the first simulation (not in the rotating frame) was: + +.. jupyter-execute:: + + results.nfev + +Whereas the number of evaluations for the second simulation in the rotating frame was: + +.. jupyter-execute:: + + rf_results.nfev + +This demonstrates that the speedup from entering the rotating frame is a result of +reducing the number of RHS calls required to solve with a given accuracy. + +2. How-to perform a rotating wave approximation, and its potential benefits +--------------------------------------------------------------------------- + +Next we show how to perform a simulation with the rotating wave approximation (RWA) +by setting the ``rwa_cutoff_freq`` argument at :class:`.Solver` instantiation, and show +how it results in further speed ups at the expense of solution accuracy. See the API +documentation for the :meth:`~qiskit_dynamics.models.rotating_wave_approximation` function +for specific details about the RWA. + +Construct a solver for the same problem, now specifying an RWA cutoff frequency and +the carrier frequencies relative to which the cutoff should be applied: + +.. jupyter-execute:: + + rwa_solver = Solver( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=[drive_hamiltonian], + rotating_frame=static_hamiltonian, + rwa_cutoff_freq=1.5 * v, + rwa_carrier_freqs=[v] + ) + + y0 = np.eye(dim, dtype=complex) + %time rwa_results = rwa_solver.solve(t_span=[0., T], y0=y0, signals=[drive_signal], atol=1e-10, rtol=1e-10) + +We observe a further reduction in time, which is a result of the solver requiring even fewer RHS +evaluations with the RWA: + +.. jupyter-execute:: + + rwa_results.nfev + +This speed comes at the cost of lower accuracy, owing to the fact that RWA is a +legitimate *approximation*, which modifies the structure of the solution: + +.. jupyter-execute:: + + U_rwa = rwa_solver.model.rotating_frame.state_out_of_frame(T, rwa_results.y[-1]) + + fidelity(U_rwa, U) + +3. How-to use a sparse evaluation mode, and how-to appropriately set a rotating frame to preserve sparsity +---------------------------------------------------------------------------------------------------------- + +Here we show how to perform a simulation using sparse arrays for evaluating the RHS +via the ``evaluation_mode`` initialization kwarg, +with extra emphasis on the following: + +.. note:: + + As stated in the + :ref:`evaluation modes section of the Models API documentation `, + when using a sparse evaluation mode, to preserve sparsity, it is recommended to + only use *diagonal* rotating frames, which can be specified as a 1d array to the + ``rotating_frame`` kwarg of :class:`.Solver` instantiation. + +For this section we use JAX as it is more performant. See the +:ref:`userguide on using JAX ` for a more detailed +explanation of how to work with JAX in Qiskit Dynamics. + +.. note:: + + JAX sparse arrays are only recommended for use on CPU. + +Start off by configuring to use JAX. + +.. jupyter-execute:: + + from qiskit_dynamics.array import Array + + # configure jax to use 64 bit mode + import jax + jax.config.update("jax_enable_x64", True) + + # tell JAX we are using CPU + jax.config.update('jax_platform_name', 'cpu') + + # set default backend + Array.set_default_backend('jax') + +Reconstruct the model pieces at a much larger dimension, to observe the +benefits of using sparse arrays. Furthermore, set up the initial state to +be a single column vector, to +further highlight the benefits of the sparse representation. + +.. jupyter-execute:: + + dim = 300 + + v = 5. + anharm = -0.33 + r = 0.02 + + a = np.diag(np.sqrt(np.arange(1, dim, dtype=complex)), 1) + adag = np.diag(np.sqrt(np.arange(1, dim, dtype=complex)), -1) + N = np.diag(np.arange(dim, dtype=complex)) + + static_hamiltonian = 2 * np.pi * v * N + np.pi * anharm * N * (N - np.eye(dim)) + drive_hamiltonian = 2 * np.pi * r * (a + adag) + drive_signal = Signal(Array(1.), carrier_freq=v) + + y0 = np.zeros(dim, dtype=complex) + y0[1] = 1. + + T = 1 / r + +Construct standard dense solver in the rotating frame of the static +Hamiltonian, define a function to solve the system for a given +amplitude, and just-in-time compile it using JAX. + +.. jupyter-execute:: + + solver = Solver( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=[drive_hamiltonian], + rotating_frame=static_hamiltonian + ) + + def dense_func(amp): + drive_signal = Signal(Array(amp), carrier_freq=v) + res = solver.solve( + t_span=[0., T], + y0=y0, + signals=[drive_signal], + method='jax_odeint', + atol=1e-10, + rtol=1e-10 + ) + return res.y[-1] + + jitted_dense_func = jax.jit(dense_func) + +Construct sparse solver **in the frame of the diagonal of the static +Hamiltonian**, define a function to solve the system for a given amplitude, +and just-in-time compile it. Note that in this case the static Hamiltonian is already +diagonal, but we explicitly highlight the need for this. + +.. jupyter-execute:: + + sparse_solver = Solver(static_hamiltonian=static_hamiltonian, + hamiltonian_operators=[drive_hamiltonian], + rotating_frame=np.diag(static_hamiltonian), + evaluation_mode='sparse') + + def sparse_func(amp): + drive_signal = Signal(Array(amp), carrier_freq=v) + res = sparse_solver.solve( + t_span=[0., T], + y0=y0, + signals = [drive_signal], + method='jax_odeint', + atol=1e-10, + rtol=1e-10 + ) + return res.y[-1] + + jitted_sparse_func = jax.jit(sparse_func) + +Run the dense simulation (twice to see the true compiled speed). + +.. jupyter-execute:: + + yf = jitted_dense_func(1.).block_until_ready() + %time yf = jitted_dense_func(1.).block_until_ready() + +Run the sparse solver (twice to see the true compiled speed). + +.. jupyter-execute:: + + yf_sparse = jitted_sparse_func(1.).block_until_ready() + %time yf_sparse = jitted_sparse_func(1.).block_until_ready() + +Verify equality of the results in a common frame. + +.. jupyter-execute:: + + yf = solver.model.rotating_frame.state_out_of_frame(T, yf) + yf_sparse = sparse_solver.model.rotating_frame.state_out_of_frame(T, yf_sparse) + + np.linalg.norm(yf - yf_sparse) + +We observe that the final states are extremely close, and that the +sparse representation provides a speed advantage for this problem. diff --git a/stable/0.4/_sources/userguide/how_to_use_jax.rst.txt b/stable/0.4/_sources/userguide/how_to_use_jax.rst.txt new file mode 100644 index 000000000..905013e0a --- /dev/null +++ b/stable/0.4/_sources/userguide/how_to_use_jax.rst.txt @@ -0,0 +1,250 @@ +.. _how-to use jax: + +How-to use JAX with ``qiskit-dynamics`` +======================================= + +JAX enables just-in-time compilation, automatic differentation, and GPU +execution. JAX is integrated into ``qiskit-dynamics`` via the +:class:`.Array` class, which allows most parts of the package to be +executed with either ``numpy`` or ``jax.numpy``. + +This guide addresses the following topics: + +1. How do I configure dynamics to run with JAX? +2. How do I write code using dispatch that can be executed with either + ``numpy`` or JAX? +3. How do I write JAX-transformable functions using the objects and + functions in ``qiskit-dynamics``? +4. Gotchas when using JAX with dynamics. + +1. How do I configure dynamics to run with JAX? +----------------------------------------------- + +The :class:`.Array` class provides a means of controlling whether array +operations are performed using ``numpy`` or ``jax.numpy``. In many +cases, the “default backend” is used to determine which of the two +options is used. + +.. jupyter-execute:: + + # configure jax to use 64 bit mode + import jax + jax.config.update("jax_enable_x64", True) + + # tell JAX we are using CPU + jax.config.update('jax_platform_name', 'cpu') + + # import Array and set default backend + from qiskit_dynamics.array import Array + Array.set_default_backend('jax') + +The default backend can be observed via: + +.. jupyter-execute:: + + Array.default_backend() + + +2. How do I write code using Array that can be executed with either ``numpy`` or JAX? +------------------------------------------------------------------------------------- + +The ``Array`` class wraps both ``numpy`` and ``jax.numpy`` +arrays. The particular type is indicated by the ``backend`` property, +and ``numpy`` functions called on an :class:`.Array` will automatically be +dispatched to ``numpy`` or ``jax.numpy`` based on the :class:`.Array`\ ’s +backend. See the API documentation for ``qiskit_dynamics.array`` for +details. + +3. How do I write JAX-transformable functions using the objects and functions in ``qiskit-dynamics``? +----------------------------------------------------------------------------------------------------- + +JAX-transformable functions must be: + + - JAX-executable. + - Take JAX arrays as input and output (see the + `JAX documentation `__ + for more details on accepted input and output types). + - Pure, in the sense that they have no side-effects. + +The previous section shows how to handle the first two points using +:class:`.Array`. The last point further restricts the type of +code that can be safely transformed. Qiskit Dynamics uses various objects which +can be updated by setting properties (models, solvers). If a function to +be transformed requires updating an already-constructed object of this +form, it is necessary to first make a *copy*. + +We demonstrate this process for both just-in-time compilation and +automatic differentiation in the context of an anticipated common +use-case: parameterized simulation of a model of a quantum system. + +3.1 Just-in-time compiling a parameterized simulation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +"Just-in-time compiling" a function means to compile it at run time. Just-in-time compilation +incurs an initial cost associated with the construction of the compiled function, +but subsequent calls to the function will generally be faster than the uncompiled version. +In JAX, just-in-time compilation is performed using the ``jax.jit`` function, +which transforms a JAX-compatible function into optimized code using +`XLA `__. We demonstrate here how, using the JAX backend, +functions built using Qiskit Dynamics can be +just-in-time compiled, resulting in faster simulation times. + +For convenience, the ``wrap`` function can be used to transform +``jax.jit`` to also work on functions that have :class:`.Array` objects as +inputs and outputs. + +.. jupyter-execute:: + + from qiskit_dynamics.array import wrap + + jit = wrap(jax.jit, decorator=True) + +Construct a :class:`.Solver` instance with a model that will be used to solve. + +.. jupyter-execute:: + + import numpy as np + from qiskit.quantum_info import Operator + from qiskit_dynamics import Solver, Signal + from qiskit_dynamics.array import Array + + r = 0.5 + w = 1. + X = Operator.from_label('X') + Z = Operator.from_label('Z') + + static_hamiltonian = 2 * np.pi * w * Z/2 + hamiltonian_operators = [2 * np.pi * r * X/2] + + solver = Solver( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=hamiltonian_operators, + rotating_frame=static_hamiltonian + ) + + +Next, define the function to be compiled: + + - The input is the amplitude of a constant-envelope signal on resonance, driven over time + :math:`[0, 3]`. + - The output is the state of the system, starting in the ground state, at + ``100`` points over the total evolution time. + +Note, as described at the beginning of this section, we need to make a copy of ``solver`` +before setting the signals, to ensure the simulation function remains pure. + +.. jupyter-execute:: + + def sim_function(amp): + + # define a constant signal + amp = Array(amp) + signals = [Signal(amp, carrier_freq=w)] + + # simulate and return results + results = solver.solve( + t_span=[0, 3.], + y0=np.array([0., 1.], dtype=complex), + signals=signals, + t_eval=np.linspace(0, 3., 100), + method='jax_odeint' + ) + + return results.y + +Compile the function. + +.. jupyter-execute:: + + fast_sim = jit(sim_function) + +The first time the function is called, JAX will compile an +`XLA `__ version of the function, which is then executed. +Hence, the time taken on the first call *includes* compilation time. + +.. jupyter-execute:: + + %time ys = fast_sim(1.).block_until_ready() + + +On subsequent calls the compiled function is directly executed, +demonstrating the true speed of the compiled function. + +.. jupyter-execute:: + + %timeit fast_sim(1.).block_until_ready() + + +We use this function to plot the :math:`Z` expectation value over a +range of input amplitudes. + +.. jupyter-execute:: + + import matplotlib.pyplot as plt + + for amp in np.linspace(0, 1, 10): + ys = fast_sim(amp) + plt.plot(np.linspace(0, 3., 100), np.real(np.abs(ys[:, 0])**2-np.abs(ys[:, 1])**2)) + + +3.2 Automatically differentiating a parameterized simulation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Next, we use ``jax.grad`` to automatically differentiate a parameterized +simulation. In this case, ``jax.grad`` requires that the output be a +real number, so we specifically compute the population in the excited +state at the end of the previous simulation + +.. jupyter-execute:: + + def excited_state_pop(amp): + yf = sim_function(amp)[-1] + return np.abs(Array(yf[0]))**2 + +Wrap ``jax.grad`` in the same way, then differentiate and compile +``excited_state_pop``. + +.. jupyter-execute:: + + grad = wrap(jax.grad, decorator=True) + + excited_pop_grad = jit(grad(excited_state_pop)) + +As before, the first execution includes compilation time. + +.. jupyter-execute:: + + %time excited_pop_grad(1.).block_until_ready() + + +Subsequent runs of the function reveal the execution time once compiled. + +.. jupyter-execute:: + + %timeit excited_pop_grad(1.).block_until_ready() + + +4. Pitfalls when using JAX with Dynamics +---------------------------------------- + +4.1 JAX must be set as the default backend before building any objects in Qiskit Dynamics +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To get dynamics to run with JAX, it is necessary to configure dynamics +to run with JAX *before* building any objects or running any functions. +The internal behaviour of some objects is modified by what the default +backend is *at the time of instantiation*. For example, at instantiation +the operators in a model or :class:`.Solver` instance will be wrapped in an +:class:`.Array` whose backend is the current default backend, and changing the +default backend after building the object won’t change this. + +4.2 Running Dynamics with JAX on CPU vs GPU +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Certain JAX-based features in Dynamics are primarily recommended for use only with CPU +or only with GPU. In such cases, a warning is raised if non-recommended hardware is used, +however users are not prevented from configuring Dynamics and JAX in whatever way they choose. + +Instances of such features are: + * Setting ``evaluation_mode='sparse'`` for solvers and models is only recommended for use on CPU. + * Parallel fixed step solver options in ``solve_lmde`` are recommended only for use on GPU. diff --git a/stable/0.4/_sources/userguide/how_to_use_pulse_schedule_for_jax_jit.rst.txt b/stable/0.4/_sources/userguide/how_to_use_pulse_schedule_for_jax_jit.rst.txt new file mode 100644 index 000000000..59b399f1b --- /dev/null +++ b/stable/0.4/_sources/userguide/how_to_use_pulse_schedule_for_jax_jit.rst.txt @@ -0,0 +1,145 @@ +.. _how-to use pulse schedules for jax-jit: + +How-to use pulse schedules generated by ``qiskit-pulse`` with JAX transformations +================================================================================= + +``Qiskit-pulse`` enables specification of time-dependence in quantum systems as pulse schedules, +built from sequences of a variety of instructions, including the specification of shaped pulses (see +the detailed API information about `Qiskit pulse API Reference +`__). As of ``qiskit-terra`` 0.23.0, JAX support +was added for the :class:`~qiskit.pulse.library.ScalableSymbolicPulse` class. This user guide entry +demonstrates the technical elements of utilizing this class within JAX-transformable functions. + +.. note:: + At present, only the :class:`~qiskit.pulse.library.ScalableSymbolicPulse` class is supported by + JAX, as the validation present in other pulse types, such as + :class:`~qiskit.pulse.library.Gaussian`, is not JAX-compatible. + +This guide addresses the following topics. See the :ref:`userguide on using JAX ` +for a more detailed explanation of how to work with JAX in Qiskit Dynamics. + +1. Configure to use JAX. +2. How to define a Gaussian pulse using :class:`~qiskit.pulse.library.ScalableSymbolicPulse`. +3. JAX transforming Pulse to Signal conversion involving + :class:`~qiskit.pulse.library.ScalableSymbolicPulse`. + + +1. Configure to use JAX +----------------------- + +First, configure Dynamics to use JAX. + +.. jupyter-execute:: + + # configure jax to use 64 bit mode + import jax + jax.config.update("jax_enable_x64", True) + + # tell JAX we are using CPU + jax.config.update('jax_platform_name', 'cpu') + + # import Array and set default backend + from qiskit_dynamics.array import Array + Array.set_default_backend('jax') + + +2. How to define a Gaussian pulse using :class:`~qiskit.pulse.library.ScalableSymbolicPulse` +-------------------------------------------------------------------------------------------- + +As the standard :class:`~qiskit.pulse.library.Gaussian` pulse is not JAX-compatible, to define a +Gaussian pulse to use in optimization, we need to instantiate a +:class:`~qiskit.pulse.library.ScalableSymbolicPulse` with a Gaussian parameterization. First, define +the symbolic representation in `sympy`. + +.. jupyter-execute:: + + from qiskit import pulse + from qiskit_dynamics.pulse import InstructionToSignals + import sympy as sym + + dt = 0.222 + w = 5. + + # Helper function that returns a lifted Gaussian symbolic equation. + def lifted_gaussian( + t: sym.Symbol, + center, + t_zero, + sigma, + ) -> sym.Expr: + t_shifted = (t - center).expand() + t_offset = (t_zero - center).expand() + + gauss = sym.exp(-((t_shifted / sigma) ** 2) / 2) + offset = sym.exp(-((t_offset / sigma) ** 2) / 2) + + return (gauss - offset) / (1 - offset) + + +Next, define the :class:`~qiskit.pulse.library.ScalableSymbolicPulse` using the above expression. + +.. jupyter-execute:: + + _t, _duration, _amp, _sigma, _angle = sym.symbols("t, duration, amp, sigma, angle") + _center = _duration / 2 + + envelope_expr = ( + _amp * sym.exp(sym.I * _angle) * lifted_gaussian(_t, _center, _duration + 1, _sigma) + ) + + gaussian_pulse = pulse.ScalableSymbolicPulse( + pulse_type="Gaussian", + duration=160, + amp=0.3, + angle=0, + parameters={"sigma": 40}, + envelope=envelope_expr, + constraints=_sigma > 0, + valid_amp_conditions=sym.Abs(_amp) <= 1.0, + ) + + gaussian_pulse.draw() + + +3. JAX transforming Pulse to Signal conversion involving :class:`~qiskit.pulse.library.ScalableSymbolicPulse` +------------------------------------------------------------------------------------------------------------- + +Using a Gaussian pulse as an example, we show that a function involving +:class:`~qiskit.pulse.library.ScalableSymbolicPulse` and the pulse to signal converter can be +JAX-compiled (or more generally, JAX-transformed). + +.. jupyter-execute:: + + # use amplitude as the function argument + def jit_func(amp): + _t, _duration, _amp, _sigma, _angle = sym.symbols("t, duration, amp, sigma, angle") + _center = _duration / 2 + + envelope_expr = ( + _amp * sym.exp(sym.I * _angle) * lifted_gaussian(_t, _center, _duration + 1, _sigma) + ) + + # we need to set disable_validation True to enable jax-jitting. + pulse.ScalableSymbolicPulse.disable_validation = True + + gaussian_pulse = pulse.ScalableSymbolicPulse( + pulse_type="Gaussian", + duration=160, + amp=amp, + angle=0, + parameters={"sigma": 40}, + envelope=envelope_expr, + constraints=_sigma > 0, + valid_amp_conditions=sym.Abs(_amp) <= 1.0, + ) + + # build a pulse schedule + with pulse.build() as schedule: + pulse.play(gaussian_pulse, pulse.DriveChannel(0)) + + # convert from a pulse schedule to a list of signals + converter = InstructionToSignals(dt, carriers={"d0": w}) + + return converter.get_signals(schedule)[0].samples.data + + jax.jit(jit_func)(0.4) diff --git a/stable/0.4/_sources/userguide/index.rst.txt b/stable/0.4/_sources/userguide/index.rst.txt new file mode 100644 index 000000000..8ff8721c2 --- /dev/null +++ b/stable/0.4/_sources/userguide/index.rst.txt @@ -0,0 +1,21 @@ +.. _qiskit-dynamics-userguide: + +.. module:: qiskit_dynamics_userguide + +========================== +Qiskit Dynamics User Guide +========================== + +.. toctree:: + :maxdepth: 2 + + how_to_use_jax.rst + how_to_configure_simulations.rst + perturbative_solvers.rst + how_to_use_pulse_schedule_for_jax_jit.rst + + +.. Hiding - Indices and tables + :ref:`genindex` + :ref:`modindex` + :ref:`search` diff --git a/stable/0.4/_sources/userguide/perturbative_solvers.rst.txt b/stable/0.4/_sources/userguide/perturbative_solvers.rst.txt new file mode 100644 index 000000000..df9c15417 --- /dev/null +++ b/stable/0.4/_sources/userguide/perturbative_solvers.rst.txt @@ -0,0 +1,331 @@ +How-to use Dyson and Magnus based solvers +========================================= + +.. warning:: + + This is an advanced topic --- utilizing perturbation-theory based solvers + requires detailed knowledge of the structure of the differential equations + involved, as well as manual tuning of the solver parameters. + See the :class:`.DysonSolver` and :class:`.MagnusSolver` documentation for API details. + Also, see :footcite:`puzzuoli_sensitivity_2022` for a detailed explanation of the solvers, + which varies and builds on the core idea introduced in :footcite:`shillito_fast_2020`. + +.. note:: + + The circumstances under which perturbative solvers outperform + traditional solvers, and which parameter sets to use, is nuanced. + Perturbative solvers executed with JAX are setup to use more parallelization within a + single solver run than typical solvers, and thus it is circumstance-specific whether + the trade-off between speed of a single run and resource consumption is advantageous. + Due to the parallelized nature, the comparison of execution times demonstrated in this + userguide are highly hardware-dependent. + + +In this tutorial we walk through how to use perturbation-theory based solvers. For +information on how these solvers work, see the :class:`.DysonSolver` and :class:`.MagnusSolver` +class documentation, as well as the perturbative expansion background information provided in +:ref:`Time-dependent perturbation theory and multi-variable +series expansions review `. + +We use a simple transmon model: + +.. math:: H(t) = 2 \pi \nu N + \pi \alpha N(N-I) + s(t) \times 2 \pi r (a + a^\dagger) + +where: + +- :math:`N`, :math:`a`, and :math:`a^\dagger` are, respectively, the + number, annihilation, and creation operators. +- :math:`\nu` is the qubit frequency and :math:`r` is the drive + strength. +- :math:`s(t)` is the drive signal, which we will take to be on + resonance with envelope :math:`f(t) = A \frac{4t (T - t)}{T^2}` + for a given amplitude :math:`A` and total time :math:`T`. + +We will walk through the following steps: + +1. Configure ``qiskit-dynamics`` to work with JAX. +2. Construct the model. +3. How-to construct and simulate using the Dyson-based perturbative solver. +4. Simulate using a traditional ODE solver, comparing speed. +5. How-to construct and simulate using the Magnus-based perturbative solver. + +1. Configure to use JAX +----------------------- + +These simulations will be done with JAX array backend to enable +compilation. See the :ref:`userguide on using JAX ` for a more detailed +explanation of how to work with JAX in Qiskit Dynamics. + +.. jupyter-execute:: + + from qiskit_dynamics.array import Array + + # configure jax to use 64 bit mode + import jax + jax.config.update("jax_enable_x64", True) + + # tell JAX we are using CPU if using a system without a GPU + jax.config.update('jax_platform_name', 'cpu') + + # set default backend + Array.set_default_backend('jax') + +2. Construct the model +---------------------- + +First, we construct the model described in the introduction. We use a relatively +high dimension for the oscillator system state space to accentuate the speed +difference between the perturbative solvers and the traditional ODE solver. The higher +dimensionality introduces higher frequencies into the model, which will +slow down both the ODE solver and the initial construction of the perturbative solver. However +after the initial construction, the higher frequencies in the model have no impact +on the perturbative solver speed. + +.. jupyter-execute:: + + import numpy as np + + dim = 10 # Oscillator dimension + + v = 5. # Transmon frequency in GHz + anharm = -0.33 # Transmon anharmonicity in GHz + r = 0.02 # Transmon drive coupling in GHz + + # Construct cavity operators + a = np.diag(np.sqrt(np.arange(1, dim)), 1) + adag = np.diag(np.sqrt(np.arange(1, dim)), -1) + N = np.diag(np.arange(dim)) + + # Static part of Hamiltonian + static_hamiltonian = 2 * np.pi * v * N + np.pi * anharm * N * (N - np.eye(dim)) + # Drive term of Hamiltonian + drive_hamiltonian = 2 * np.pi * r * (a + adag) + + # total simulation time + T = 1. / r + + # Drive envelope function + envelope_func = lambda t: t * (T - t) / (T**2 / 4) + +3. How-to construct and simulate using the Dyson-based perturbative solver +-------------------------------------------------------------------------- + +Setting up a :class:`.DysonSolver` requires more setup than the standard +:class:`.Solver`, as the user must specify several configuration parameters, +along with the structure of the differential equation: + +- The :class:`.DysonSolver` requires direct specification of the LMDE to the + solver. If we are simulating the Schrodinger equation, we need to + multiply the Hamiltonian terms by ``-1j`` when describing the LMDE operators. +- The :class:`.DysonSolver` is a fixed step solver, with the step size + being fixed at instantiation. This step size must be chosen in conjunction + with the ``expansion_order`` to ensure that a suitable accuracy is attained. +- Over each fixed time-step the :class:`.DysonSolver` solves by computing a + truncated perturbative expansion. + + - To compute the truncated perturbative expansion, the signal envelopes are + approximated as a linear combination of Chebyshev polynomials. + - The order of the Chebyshev approximations, along with central carrier frequencies + for defining the “envelope” of each :class:`.Signal`, must be provided at instantiation. + +See the :class:`.DysonSolver` API docs for more details. + +For our example Hamiltonian we configure the :class:`.DysonSolver` as follows: + +.. jupyter-execute:: + + %%time + + from qiskit_dynamics import DysonSolver + + dt = 0.1 + dyson_solver = DysonSolver( + operators=[-1j * drive_hamiltonian], + rotating_frame=-1j * static_hamiltonian, + dt=dt, + carrier_freqs=[v], + chebyshev_orders=[1], + expansion_order=7, + integration_method='jax_odeint', + atol=1e-12, + rtol=1e-12 + ) + +The above parameters are chosen so that the :class:`.DysonSolver` is fast and produces +high accuracy solutions (measured and confirmed after the fact). The relatively large +step size ``dt = 0.1`` is chosen for speed: the larger the step size, the fewer steps required. +To ensure high accuracy given the large step size, we choose a high expansion order, +and utilize a linear envelope approximation scheme by setting the ``chebyshev_order`` to ``1`` +for the single drive signal. + +Similar to the :class:`.Solver` interface, the :meth:`.DysonSolver.solve` method can be +called to simulate the system for a given list of signals, initial state, start time, +and number of time steps of length ``dt``. + +To properly compare the speed of :class:`.DysonSolver` to a traditional ODE solver, +we write JAX-compilable functions wrapping each that, given an amplitude value, +returns the final unitary over the interval ``[0, (T // dt) * dt]`` for an on-resonance +drive with envelope shape given by ``envelope_func`` above. Running compiled versions of +these functions gives a sense of the speeds attainable by these solvers. + +.. jupyter-execute:: + + from qiskit_dynamics import Signal + from jax import jit + + # Jit the function to improve performance for repeated calls + @jit + def dyson_sim(amp): + """For a given envelope amplitude, simulate the final unitary using the + Dyson solver. + """ + drive_signal = Signal(lambda t: Array(amp) * envelope_func(t), carrier_freq=v) + return dyson_solver.solve( + signals=[drive_signal], + y0=np.eye(dim, dtype=complex), + t0=0., + n_steps=int(T // dt) + ).y[-1] + +First run includes compile time. + +.. jupyter-execute:: + + %time yf_dyson = dyson_sim(1.).block_until_ready() + + +Once JIT compilation has been performance we can benchmark the performance of the +JIT-compiled solver: + +.. jupyter-execute:: + + %time yf_dyson = dyson_sim(1.).block_until_ready() + + +4. Comparison to traditional ODE solver +--------------------------------------- + +We now construct the same simulation using a standard solver to compare +accuracy and simulation speed. + +.. jupyter-execute:: + + from qiskit_dynamics import Solver + + solver = Solver( + static_hamiltonian=static_hamiltonian, + hamiltonian_operators=[drive_hamiltonian], + rotating_frame=static_hamiltonian + ) + + # specify tolerance as an argument to run the simulation at different tolerances + def ode_sim(amp, tol): + drive_signal = Signal(lambda t: Array(amp) * envelope_func(t), carrier_freq=v) + res = solver.solve( + t_span=[0., int(T // dt) * dt], + y0=np.eye(dim, dtype=complex), + signals=[drive_signal], + method='jax_odeint', + atol=tol, + rtol=tol + ) + return res.y[-1] + +Simulate with low tolerance for comparison to high accuracy solution. + +.. jupyter-execute:: + + yf_low_tol = ode_sim(1., 1e-13) + np.linalg.norm(yf_low_tol - yf_dyson) + + +For speed comparison, compile at a tolerance with similar accuracy. + +.. jupyter-execute:: + + jit_ode_sim = jit(lambda amp: ode_sim(amp, 1e-8)) + + %time yf_ode = jit_ode_sim(1.).block_until_ready() + +Measure compiled time. + +.. jupyter-execute:: + + %time yf_ode = jit_ode_sim(1.).block_until_ready() + + +Confirm similar accuracy solution. + +.. jupyter-execute:: + + np.linalg.norm(yf_low_tol - yf_ode) + +Here we see that, once compiled, the Dyson-based solver has a +significant speed advantage over the traditional solver, at the expense +of the initial compilation time and the technical aspect of using the solver. + +5. How-to construct and simulate using the Magnus-based perturbation solver +--------------------------------------------------------------------------- + +Next, we repeat our example using the Magnus-based perturbative solver. +Setup of the :class:`.MagnusSolver` is similar to the :class:`.DysonSolver`, +but it uses the Magnus expansion and matrix exponentiation to simulate over +each fixed time step. + +.. jupyter-execute:: + + %%time + + from qiskit_dynamics import MagnusSolver + + dt = 0.1 + magnus_solver = MagnusSolver( + operators=[-1j * drive_hamiltonian], + rotating_frame=-1j * static_hamiltonian, + dt=dt, + carrier_freqs=[v], + chebyshev_orders=[1], + expansion_order=3, + integration_method='jax_odeint', + atol=1e-12, + rtol=1e-12 + ) + + +Setup simulation function. + +.. jupyter-execute:: + + @jit + def magnus_sim(amp): + drive_signal = Signal(lambda t: Array(amp) * envelope_func(t), carrier_freq=v) + return magnus_solver.solve( + signals=[drive_signal], + y0=np.eye(dim, dtype=complex), + t0=0., + n_steps=int(T // dt) + ).y[-1] + + +First run includes compile time. + +.. jupyter-execute:: + + %time yf_magnus = magnus_sim(1.).block_until_ready() + +Second run demonstrates speed of the simulation. + +.. jupyter-execute:: + + %time yf_magnus = magnus_sim(1.).block_until_ready() + + +.. jupyter-execute:: + + np.linalg.norm(yf_magnus - yf_low_tol) + + +Observe comparable accuracy at a lower order in the expansion, albeit +with a modest speed up as compared to the Dyson-based solver. + +.. footbibliography:: diff --git a/stable/0.4/_static/_sphinx_javascript_frameworks_compat.js b/stable/0.4/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 000000000..81415803e --- /dev/null +++ b/stable/0.4/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/stable/0.4/_static/basic.css b/stable/0.4/_static/basic.css new file mode 100644 index 000000000..30fee9d0f --- /dev/null +++ b/stable/0.4/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 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: 230px; + 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/stable/0.4/_static/debug.css b/stable/0.4/_static/debug.css new file mode 100644 index 000000000..74d4aec33 --- /dev/null +++ b/stable/0.4/_static/debug.css @@ -0,0 +1,69 @@ +/* + This CSS file should be overridden by the theme authors. It's + meant for debugging and developing the skeleton that this theme provides. +*/ +body { + font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji"; + background: lavender; +} +.sb-announcement { + background: rgb(131, 131, 131); +} +.sb-announcement__inner { + background: black; + color: white; +} +.sb-header { + background: lightskyblue; +} +.sb-header__inner { + background: royalblue; + color: white; +} +.sb-header-secondary { + background: lightcyan; +} +.sb-header-secondary__inner { + background: cornflowerblue; + color: white; +} +.sb-sidebar-primary { + background: lightgreen; +} +.sb-main { + background: blanchedalmond; +} +.sb-main__inner { + background: antiquewhite; +} +.sb-header-article { + background: lightsteelblue; +} +.sb-article-container { + background: snow; +} +.sb-article-main { + background: white; +} +.sb-footer-article { + background: lightpink; +} +.sb-sidebar-secondary { + background: lightgoldenrodyellow; +} +.sb-footer-content { + background: plum; +} +.sb-footer-content__inner { + background: palevioletred; +} +.sb-footer { + background: pink; +} +.sb-footer__inner { + background: salmon; +} +.sb-article { + background: white; +} diff --git a/stable/0.4/_static/doctools.js b/stable/0.4/_static/doctools.js new file mode 100644 index 000000000..d06a71d75 --- /dev/null +++ b/stable/0.4/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 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/stable/0.4/_static/documentation_options.js b/stable/0.4/_static/documentation_options.js new file mode 100644 index 000000000..065334eb7 --- /dev/null +++ b/stable/0.4/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '0.4.5', + 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/stable/0.4/_static/file.png b/stable/0.4/_static/file.png new file mode 100644 index 000000000..a858a410e Binary files /dev/null and b/stable/0.4/_static/file.png differ diff --git a/stable/0.4/_static/images/ecosystem-logo.svg b/stable/0.4/_static/images/ecosystem-logo.svg new file mode 100644 index 000000000..51b02dd2d --- /dev/null +++ b/stable/0.4/_static/images/ecosystem-logo.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/stable/0.4/_static/images/logo.png b/stable/0.4/_static/images/logo.png new file mode 100644 index 000000000..769ddee14 Binary files /dev/null and b/stable/0.4/_static/images/logo.png differ diff --git a/stable/0.4/_static/jquery.js b/stable/0.4/_static/jquery.js new file mode 100644 index 000000000..c4c6022f2 --- /dev/null +++ b/stable/0.4/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0=0;o--)(s=e[o])&&(r=(a<3?s(r):a>3?s(t,i,r):s(t,i))||r);return a>3&&r&&Object.defineProperty(t,i,r),r}function i(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)} +/** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const n=window,s=n.ShadowRoot&&(void 0===n.ShadyCSS||n.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,a=Symbol(),r=new WeakMap;let o=class{constructor(e,t,i){if(this._$cssResult$=!0,i!==a)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const t=this.t;if(s&&void 0===e){const i=void 0!==t&&1===t.length;i&&(e=r.get(t)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),i&&r.set(t,e))}return e}toString(){return this.cssText}};const d=s?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new o("string"==typeof e?e:e+"",void 0,a))(t)})(e):e +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */;var l;const c=window,h=c.trustedTypes,u=h?h.emptyScript:"",p=c.reactiveElementPolyfillSupport,v={toAttribute(e,t){switch(t){case Boolean:e=e?u:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},m=(e,t)=>t!==e&&(t==t||e==e),b={attribute:!0,type:String,converter:v,reflect:!1,hasChanged:m};let f=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u()}static addInitializer(e){var t;this.finalize(),(null!==(t=this.h)&&void 0!==t?t:this.h=[]).push(e)}static get observedAttributes(){this.finalize();const e=[];return this.elementProperties.forEach(((t,i)=>{const n=this._$Ep(i,t);void 0!==n&&(this._$Ev.set(n,i),e.push(n))})),e}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this.finalize(),this.elementProperties.set(e,t),!t.noAccessor&&!this.prototype.hasOwnProperty(e)){const i="symbol"==typeof e?Symbol():"__"+e,n=this.getPropertyDescriptor(e,i,t);void 0!==n&&Object.defineProperty(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){return{get(){return this[t]},set(n){const s=this[e];this[t]=n,this.requestUpdate(e,s,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)||b}static finalize(){if(this.hasOwnProperty("finalized"))return!1;this.finalized=!0;const e=Object.getPrototypeOf(this);if(e.finalize(),void 0!==e.h&&(this.h=[...e.h]),this.elementProperties=new Map(e.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const e=this.properties,t=[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)];for(const i of t)this.createProperty(i,e[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(d(e))}else void 0!==e&&t.push(d(e));return t}static _$Ep(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}u(){var e;this._$E_=new Promise((e=>this.enableUpdating=e)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(e=this.constructor.h)||void 0===e||e.forEach((e=>e(this)))}addController(e){var t,i;(null!==(t=this._$ES)&&void 0!==t?t:this._$ES=[]).push(e),void 0!==this.renderRoot&&this.isConnected&&(null===(i=e.hostConnected)||void 0===i||i.call(e))}removeController(e){var t;null===(t=this._$ES)||void 0===t||t.splice(this._$ES.indexOf(e)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((e,t)=>{this.hasOwnProperty(t)&&(this._$Ei.set(t,this[t]),delete this[t])}))}createRenderRoot(){var e;const t=null!==(e=this.shadowRoot)&&void 0!==e?e:this.attachShadow(this.constructor.shadowRootOptions);return((e,t)=>{s?e.adoptedStyleSheets=t.map((e=>e instanceof CSSStyleSheet?e:e.styleSheet)):t.forEach((t=>{const i=document.createElement("style"),s=n.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=t.cssText,e.appendChild(i)}))})(t,this.constructor.elementStyles),t}connectedCallback(){var e;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this._$ES)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostConnected)||void 0===t?void 0:t.call(e)}))}enableUpdating(e){}disconnectedCallback(){var e;null===(e=this._$ES)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostDisconnected)||void 0===t?void 0:t.call(e)}))}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$EO(e,t,i=b){var n;const s=this.constructor._$Ep(e,i);if(void 0!==s&&!0===i.reflect){const a=(void 0!==(null===(n=i.converter)||void 0===n?void 0:n.toAttribute)?i.converter:v).toAttribute(t,i.type);this._$El=e,null==a?this.removeAttribute(s):this.setAttribute(s,a),this._$El=null}}_$AK(e,t){var i;const n=this.constructor,s=n._$Ev.get(e);if(void 0!==s&&this._$El!==s){const e=n.getPropertyOptions(s),a="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==(null===(i=e.converter)||void 0===i?void 0:i.fromAttribute)?e.converter:v;this._$El=s,this[s]=a.fromAttribute(t,e.type),this._$El=null}}requestUpdate(e,t,i){let n=!0;void 0!==e&&(((i=i||this.constructor.getPropertyOptions(e)).hasChanged||m)(this[e],t)?(this._$AL.has(e)||this._$AL.set(e,t),!0===i.reflect&&this._$El!==e&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(e,i))):n=!1),!this.isUpdatePending&&n&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var e;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((e,t)=>this[t]=e)),this._$Ei=void 0);let t=!1;const i=this._$AL;try{t=this.shouldUpdate(i),t?(this.willUpdate(i),null===(e=this._$ES)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostUpdate)||void 0===t?void 0:t.call(e)})),this.update(i)):this._$Ek()}catch(e){throw t=!1,this._$Ek(),e}t&&this._$AE(i)}willUpdate(e){}_$AE(e){var t;null===(t=this._$ES)||void 0===t||t.forEach((e=>{var t;return null===(t=e.hostUpdated)||void 0===t?void 0:t.call(e)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(e){return!0}update(e){void 0!==this._$EC&&(this._$EC.forEach(((e,t)=>this._$EO(t,this[t],e))),this._$EC=void 0),this._$Ek()}updated(e){}firstUpdated(e){}}; +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +var g;f.finalized=!0,f.elementProperties=new Map,f.elementStyles=[],f.shadowRootOptions={mode:"open"},null==p||p({ReactiveElement:f}),(null!==(l=c.reactiveElementVersions)&&void 0!==l?l:c.reactiveElementVersions=[]).push("1.6.1");const x=window,_=x.trustedTypes,y=_?_.createPolicy("lit-html",{createHTML:e=>e}):void 0,k=`lit$${(Math.random()+"").slice(9)}$`,w="?"+k,$=`<${w}>`,S=document,A=(e="")=>S.createComment(e),z=e=>null===e||"object"!=typeof e&&"function"!=typeof e,C=Array.isArray,E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,P=/-->/g,q=/>/g,M=RegExp(">|[ \t\n\f\r](?:([^\\s\"'>=/]+)([ \t\n\f\r]*=[ \t\n\f\r]*(?:[^ \t\n\f\r\"'`<>=]|(\"|')|))|$)","g"),T=/'/g,N=/"/g,O=/^(?:script|style|textarea|title)$/i,R=(e=>(t,...i)=>({_$litType$:e,strings:t,values:i}))(1),I=Symbol.for("lit-noChange"),U=Symbol.for("lit-nothing"),H=new WeakMap,j=S.createTreeWalker(S,129,null,!1),L=(e,t)=>{const i=e.length-1,n=[];let s,a=2===t?"":"",r=E;for(let t=0;t"===d[0]?(r=null!=s?s:E,l=-1):void 0===d[1]?l=-2:(l=r.lastIndex-d[2].length,o=d[1],r=void 0===d[3]?M:'"'===d[3]?N:T):r===N||r===T?r=M:r===P||r===q?r=E:(r=M,s=void 0);const h=r===M&&e[t+1].startsWith("/>")?" ":"";a+=r===E?i+$:l>=0?(n.push(o),i.slice(0,l)+"$lit$"+i.slice(l)+k+h):i+k+(-2===l?(n.push(void 0),t):h)}const o=a+(e[i]||"")+(2===t?"":"");if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return[void 0!==y?y.createHTML(o):o,n]};class V{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let s=0,a=0;const r=e.length-1,o=this.parts,[d,l]=L(e,t);if(this.el=V.createElement(d,i),j.currentNode=this.el.content,2===t){const e=this.el.content,t=e.firstChild;t.remove(),e.append(...t.childNodes)}for(;null!==(n=j.nextNode())&&o.length0){n.textContent=_?_.emptyScript:"";for(let i=0;iC(e)||"function"==typeof(null==e?void 0:e[Symbol.iterator]))(e)?this.k(e):this.g(e)}O(e,t=this._$AB){return this._$AA.parentNode.insertBefore(e,t)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}g(e){this._$AH!==U&&z(this._$AH)?this._$AA.nextSibling.data=e:this.T(S.createTextNode(e)),this._$AH=e}$(e){var t;const{values:i,_$litType$:n}=e,s="number"==typeof n?this._$AC(e):(void 0===n.el&&(n.el=V.createElement(n.h,this.options)),n);if((null===(t=this._$AH)||void 0===t?void 0:t._$AD)===s)this._$AH.p(i);else{const e=new D(s,this),t=e.v(this.options);e.p(i),this.T(t),this._$AH=e}}_$AC(e){let t=H.get(e.strings);return void 0===t&&H.set(e.strings,t=new V(e)),t}k(e){C(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const s of e)n===t.length?t.push(i=new F(this.O(A()),this.O(A()),this,this.options)):i=t[n],i._$AI(s),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=U}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(e,t=this,i,n){const s=this.strings;let a=!1;if(void 0===s)e=B(this,e,t,0),a=!z(e)||e!==this._$AH&&e!==I,a&&(this._$AH=e);else{const n=e;let r,o;for(e=s[0],r=0;r{var n,s;const a=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:t;let r=a._$litPart$;if(void 0===r){const e=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:null;a._$litPart$=r=new F(t.insertBefore(A(),e),e,void 0,null!=i?i:{})}return r._$AI(e),r})(t,this.renderRoot,this.renderOptions)}connectedCallback(){var e;super.connectedCallback(),null===(e=this._$Do)||void 0===e||e.setConnected(!0)}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this._$Do)||void 0===e||e.setConnected(!1)}render(){return I}}te.finalized=!0,te._$litElement$=!0,null===(K=globalThis.litElementHydrateSupport)||void 0===K||K.call(globalThis,{LitElement:te});const ie=globalThis.litElementPolyfillSupport;null==ie||ie({LitElement:te}),(null!==(ee=globalThis.litElementVersions)&&void 0!==ee?ee:globalThis.litElementVersions=[]).push("3.2.2"); +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +const ne=e=>t=>"function"==typeof t?((e,t)=>(customElements.define(e,t),t))(e,t):((e,t)=>{const{kind:i,elements:n}=t;return{kind:i,elements:n,finisher(t){customElements.define(e,t)}}})(e,t) +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */,se=(e,t)=>"method"===t.kind&&t.descriptor&&!("value"in t.descriptor)?{...t,finisher(i){i.createProperty(t.key,e)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:t.key,initializer(){"function"==typeof t.initializer&&(this[t.key]=t.initializer.call(this))},finisher(i){i.createProperty(t.key,e)}};function ae(e){return(t,i)=>void 0!==i?((e,t,i)=>{t.constructor.createProperty(i,e)})(e,t,i):se(e,t) +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */}var re;null===(re=window.HTMLSlotElement)||void 0===re||re.prototype.assignedElements; +/** + * @license + * Copyright 2018 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +const oe=e=>null!=e?e:U;function de(e,t){(null==t||t>e.length)&&(t=e.length);for(var i=0,n=new Array(t);i=0;a--){var r=t[e.placement];r.splice(r.indexOf(e.key),1);var o=this.fromElementDescriptor(e),d=this.toElementFinisherExtras((0,s[a])(o)||o);e=d.element,this.addElementPlacement(e,t),d.finisher&&n.push(d.finisher);var l=d.extras;if(l){for(var c=0;c=0;n--){var s=this.fromClassDescriptor(e),a=this.toClassDescriptor((0,t[n])(s)||s);if(void 0!==a.finisher&&i.push(a.finisher),void 0!==a.elements){e=a.elements;for(var r=0;r(...t)=>{const i=e(...t);return _e.set(i,!0),i},ke=e=>"function"==typeof e&&_e.has(e),we="undefined"!=typeof window&&null!=window.customElements&&void 0!==window.customElements.polyfillWrapFlushCallback,$e=(e,t,i=null)=>{for(;t!==i;){const i=t.nextSibling;e.removeChild(t),t=i}},Se={},Ae={},ze=`{{lit-${String(Math.random()).slice(2)}}}`,Ce=`\x3c!--${ze}--\x3e`,Ee=new RegExp(`${ze}|${Ce}`),Pe="$lit$";class qe{constructor(e,t){this.parts=[],this.element=t;const i=[],n=[],s=document.createTreeWalker(t.content,133,null,!1);let a=0,r=-1,o=0;const{strings:d,values:{length:l}}=e;for(;o0;){const t=d[o],i=Oe.exec(t)[2],n=i.toLowerCase()+Pe,s=e.getAttribute(n);e.removeAttribute(n);const a=s.split(Ee);this.parts.push({type:"attribute",index:r,name:i,strings:a}),o+=a.length-1}}"TEMPLATE"===e.tagName&&(n.push(e),s.currentNode=e.content)}else if(3===e.nodeType){const t=e.data;if(t.indexOf(ze)>=0){const n=e.parentNode,s=t.split(Ee),a=s.length-1;for(let t=0;t{const i=e.length-t.length;return i>=0&&e.slice(i)===t},Te=e=>-1!==e.index,Ne=()=>document.createComment(""),Oe=/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +class Re{constructor(e,t,i){this.__parts=[],this.template=e,this.processor=t,this.options=i}update(e){let t=0;for(const i of this.__parts)void 0!==i&&i.setValue(e[t]),t++;for(const e of this.__parts)void 0!==e&&e.commit()}_clone(){const e=we?this.template.element.content.cloneNode(!0):document.importNode(this.template.element.content,!0),t=[],i=this.template.parts,n=document.createTreeWalker(e,133,null,!1);let s,a=0,r=0,o=n.nextNode();for(;ae}),Ue=` ${ze} `;class He{constructor(e,t,i,n){this.strings=e,this.values=t,this.type=i,this.processor=n}getHTML(){const e=this.strings.length-1;let t="",i=!1;for(let n=0;n-1||i)&&-1===e.indexOf("--\x3e",s+1);const a=Oe.exec(e);t+=null===a?e+(i?Ue:Ce):e.substr(0,a.index)+a[1]+a[2]+Pe+a[3]+ze}return t+=this.strings[e],t}getTemplateElement(){const e=document.createElement("template");let t=this.getHTML();return void 0!==Ie&&(t=Ie.createHTML(t)),e.innerHTML=t,e}}class je extends He{getHTML(){return`${super.getHTML()}`}getTemplateElement(){const e=super.getTemplateElement(),t=e.content,i=t.firstChild;return t.removeChild(i),((e,t,i=null,n=null)=>{for(;t!==i;){const i=t.nextSibling;e.insertBefore(t,n),t=i}})(t,i.firstChild),e}} +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */const Le=e=>null===e||!("object"==typeof e||"function"==typeof e),Ve=e=>Array.isArray(e)||!(!e||!e[Symbol.iterator]);class Be{constructor(e,t,i){this.dirty=!0,this.element=e,this.name=t,this.strings=i,this.parts=[];for(let e=0;e{try{const e={get capture(){return Ye=!0,!1}};window.addEventListener("test",e,e),window.removeEventListener("test",e,e)}catch(e){}})();class Qe{constructor(e,t,i){this.value=void 0,this.__pendingValue=void 0,this.element=e,this.eventName=t,this.eventContext=i,this.__boundHandleEvent=e=>this.handleEvent(e)}setValue(e){this.__pendingValue=e}commit(){for(;ke(this.__pendingValue);){const e=this.__pendingValue;this.__pendingValue=Se,e(this)}if(this.__pendingValue===Se)return;const e=this.__pendingValue,t=this.value,i=null==e||null!=t&&(e.capture!==t.capture||e.once!==t.once||e.passive!==t.passive),n=null!=e&&(null==t||i);i&&this.element.removeEventListener(this.eventName,this.__boundHandleEvent,this.__options),n&&(this.__options=Je(e),this.element.addEventListener(this.eventName,this.__boundHandleEvent,this.__options)),this.value=e,this.__pendingValue=Se}handleEvent(e){"function"==typeof this.value?this.value.call(this.eventContext||this.element,e):this.value.handleEvent(e)}}const Je=e=>e&&(Ye?{capture:e.capture,passive:e.passive,once:e.once}:e.capture) +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */;const Ze=new class{handleAttributeExpressions(e,t,i,n){const s=t[0];if("."===s){return new We(e,t.slice(1),i).parts}if("@"===s)return[new Qe(e,t.slice(1),n.eventContext)];if("?"===s)return[new Xe(e,t.slice(1),i)];return new Be(e,t,i).parts}handleTextExpression(e){return new Fe(e)}}; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */function Ke(e){let t=et.get(e.type);void 0===t&&(t={stringsArray:new WeakMap,keyString:new Map},et.set(e.type,t));let i=t.stringsArray.get(e.strings);if(void 0!==i)return i;const n=e.strings.join(ze);return i=t.keyString.get(n),void 0===i&&(i=new qe(e,e.getTemplateElement()),t.keyString.set(n,i)),t.stringsArray.set(e.strings,i),i}const et=new Map,tt=new WeakMap; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +"undefined"!=typeof window&&(window.litHtmlVersions||(window.litHtmlVersions=[])).push("1.4.1");const it=(e,...t)=>new He(e,t,"html",Ze),nt=(e,...t)=>new je(e,t,"svg",Ze) +/** + * @license + * Copyright (c) 2018 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */;class st{constructor(e){this.classes=new Set,this.changed=!1,this.element=e;const t=(e.getAttribute("class")||"").split(/\s+/);for(const e of t)this.classes.add(e)}add(e){this.classes.add(e),this.changed=!0}remove(e){this.classes.delete(e),this.changed=!0}commit(){if(this.changed){let e="";this.classes.forEach((t=>e+=t+" ")),this.element.setAttribute("class",e)}}}const at=new WeakMap,rt=ye((e=>t=>{if(!(t instanceof De)||t instanceof Ge||"class"!==t.committer.name||t.committer.parts.length>1)throw new Error("The `classMap` directive must be used in the `class` attribute and must be the only part in the attribute.");const{committer:i}=t,{element:n}=i;let s=at.get(t);void 0===s&&(n.setAttribute("class",i.strings.join(" ")),at.set(t,s=new Set));const a=n.classList||new st(n);s.forEach((t=>{t in e||(a.remove(t),s.delete(t))}));for(const t in e){const i=e[t];i!=s.has(t)&&(i?(a.add(t),s.add(t)):(a.remove(t),s.delete(t)))}"function"==typeof a.commit&&a.commit()})),ot=133;function dt(e,t){const{element:{content:i},parts:n}=e,s=document.createTreeWalker(i,ot,null,!1);let a=ct(n),r=n[a],o=-1,d=0;const l=[];let c=null;for(;s.nextNode();){o++;const e=s.currentNode;for(e.previousSibling===c&&(c=null),t.has(e)&&(l.push(e),null===c&&(c=e)),null!==c&&d++;void 0!==r&&r.index===o;)r.index=null!==c?-1:r.index-d,a=ct(n,a),r=n[a]}l.forEach((e=>e.parentNode.removeChild(e)))}const lt=e=>{let t=11===e.nodeType?0:1;const i=document.createTreeWalker(e,ot,null,!1);for(;i.nextNode();)t++;return t},ct=(e,t=-1)=>{for(let i=t+1;i`${e}--${t}`;let ut=!0;void 0===window.ShadyCSS?ut=!1:void 0===window.ShadyCSS.prepareTemplateDom&&(console.warn("Incompatible ShadyCSS version detected. Please update to at least @webcomponents/webcomponentsjs@2.0.2 and @webcomponents/shadycss@1.3.1."),ut=!1);const pt=e=>t=>{const i=ht(t.type,e);let n=et.get(i);void 0===n&&(n={stringsArray:new WeakMap,keyString:new Map},et.set(i,n));let s=n.stringsArray.get(t.strings);if(void 0!==s)return s;const a=t.strings.join(ze);if(s=n.keyString.get(a),void 0===s){const i=t.getTemplateElement();ut&&window.ShadyCSS.prepareTemplateDom(i,e),s=new qe(t,i),n.keyString.set(a,s)}return n.stringsArray.set(t.strings,s),s},vt=["html","svg"],mt=new Set,bt=(e,t,i)=>{mt.add(e);const n=i?i.element:document.createElement("template"),s=t.querySelectorAll("style"),{length:a}=s;if(0===a)return void window.ShadyCSS.prepareTemplateStyles(n,e);const r=document.createElement("style");for(let e=0;e{vt.forEach((t=>{const i=et.get(ht(t,e));void 0!==i&&i.keyString.forEach((e=>{const{element:{content:t}}=e,i=new Set;Array.from(t.querySelectorAll("style")).forEach((e=>{i.add(e)})),dt(e,i)}))}))})(e);const o=n.content;i?function(e,t,i=null){const{element:{content:n},parts:s}=e;if(null==i)return void n.appendChild(t);const a=document.createTreeWalker(n,ot,null,!1);let r=ct(s),o=0,d=-1;for(;a.nextNode();)for(d++,a.currentNode===i&&(o=lt(t),i.parentNode.insertBefore(t,i));-1!==r&&s[r].index===d;){if(o>0){for(;-1!==r;)s[r].index+=o,r=ct(s,r);return}r=ct(s,r)}}(i,r,o.firstChild):o.insertBefore(r,o.firstChild),window.ShadyCSS.prepareTemplateStyles(n,e);const d=o.querySelector("style");if(window.ShadyCSS.nativeShadow&&null!==d)t.insertBefore(d.cloneNode(!0),t.firstChild);else if(i){o.insertBefore(r,o.firstChild);const e=new Set;e.add(r),dt(i,e)}};window.JSCompiler_renameProperty=(e,t)=>e;const ft={toAttribute(e,t){switch(t){case Boolean:return e?"":null;case Object:case Array:return null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){switch(t){case Boolean:return null!==e;case Number:return null===e?null:Number(e);case Object:case Array:return JSON.parse(e)}return e}},gt=(e,t)=>t!==e&&(t==t||e==e),xt={attribute:!0,type:String,converter:ft,reflect:!1,hasChanged:gt},_t="finalized";class yt extends HTMLElement{constructor(){super(),this.initialize()}static get observedAttributes(){this.finalize();const e=[];return this._classProperties.forEach(((t,i)=>{const n=this._attributeNameForProperty(i,t);void 0!==n&&(this._attributeToPropertyMap.set(n,i),e.push(n))})),e}static _ensureClassProperties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_classProperties",this))){this._classProperties=new Map;const e=Object.getPrototypeOf(this)._classProperties;void 0!==e&&e.forEach(((e,t)=>this._classProperties.set(t,e)))}}static createProperty(e,t=xt){if(this._ensureClassProperties(),this._classProperties.set(e,t),t.noAccessor||this.prototype.hasOwnProperty(e))return;const i="symbol"==typeof e?Symbol():`__${e}`,n=this.getPropertyDescriptor(e,i,t);void 0!==n&&Object.defineProperty(this.prototype,e,n)}static getPropertyDescriptor(e,t,i){return{get(){return this[t]},set(n){const s=this[e];this[t]=n,this.requestUpdateInternal(e,s,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this._classProperties&&this._classProperties.get(e)||xt}static finalize(){const e=Object.getPrototypeOf(this);if(e.hasOwnProperty(_t)||e.finalize(),this[_t]=!0,this._ensureClassProperties(),this._attributeToPropertyMap=new Map,this.hasOwnProperty(JSCompiler_renameProperty("properties",this))){const e=this.properties,t=[...Object.getOwnPropertyNames(e),..."function"==typeof Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e):[]];for(const i of t)this.createProperty(i,e[i])}}static _attributeNameForProperty(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}static _valueHasChanged(e,t,i=gt){return i(e,t)}static _propertyValueFromAttribute(e,t){const i=t.type,n=t.converter||ft,s="function"==typeof n?n:n.fromAttribute;return s?s(e,i):e}static _propertyValueToAttribute(e,t){if(void 0===t.reflect)return;const i=t.type,n=t.converter;return(n&&n.toAttribute||ft.toAttribute)(e,i)}initialize(){this._updateState=0,this._updatePromise=new Promise((e=>this._enableUpdatingResolver=e)),this._changedProperties=new Map,this._saveInstanceProperties(),this.requestUpdateInternal()}_saveInstanceProperties(){this.constructor._classProperties.forEach(((e,t)=>{if(this.hasOwnProperty(t)){const e=this[t];delete this[t],this._instanceProperties||(this._instanceProperties=new Map),this._instanceProperties.set(t,e)}}))}_applyInstanceProperties(){this._instanceProperties.forEach(((e,t)=>this[t]=e)),this._instanceProperties=void 0}connectedCallback(){this.enableUpdating()}enableUpdating(){void 0!==this._enableUpdatingResolver&&(this._enableUpdatingResolver(),this._enableUpdatingResolver=void 0)}disconnectedCallback(){}attributeChangedCallback(e,t,i){t!==i&&this._attributeToProperty(e,i)}_propertyToAttribute(e,t,i=xt){const n=this.constructor,s=n._attributeNameForProperty(e,i);if(void 0!==s){const e=n._propertyValueToAttribute(t,i);if(void 0===e)return;this._updateState=8|this._updateState,null==e?this.removeAttribute(s):this.setAttribute(s,e),this._updateState=-9&this._updateState}}_attributeToProperty(e,t){if(8&this._updateState)return;const i=this.constructor,n=i._attributeToPropertyMap.get(e);if(void 0!==n){const e=i.getPropertyOptions(n);this._updateState=16|this._updateState,this[n]=i._propertyValueFromAttribute(t,e),this._updateState=-17&this._updateState}}requestUpdateInternal(e,t,i){let n=!0;if(void 0!==e){const s=this.constructor;i=i||s.getPropertyOptions(e),s._valueHasChanged(this[e],t,i.hasChanged)?(this._changedProperties.has(e)||this._changedProperties.set(e,t),!0!==i.reflect||16&this._updateState||(void 0===this._reflectingProperties&&(this._reflectingProperties=new Map),this._reflectingProperties.set(e,i))):n=!1}!this._hasRequestedUpdate&&n&&(this._updatePromise=this._enqueueUpdate())}requestUpdate(e,t){return this.requestUpdateInternal(e,t),this.updateComplete}async _enqueueUpdate(){this._updateState=4|this._updateState;try{await this._updatePromise}catch(e){}const e=this.performUpdate();return null!=e&&await e,!this._hasRequestedUpdate}get _hasRequestedUpdate(){return 4&this._updateState}get hasUpdated(){return 1&this._updateState}performUpdate(){if(!this._hasRequestedUpdate)return;this._instanceProperties&&this._applyInstanceProperties();let e=!1;const t=this._changedProperties;try{e=this.shouldUpdate(t),e?this.update(t):this._markUpdated()}catch(t){throw e=!1,this._markUpdated(),t}e&&(1&this._updateState||(this._updateState=1|this._updateState,this.firstUpdated(t)),this.updated(t))}_markUpdated(){this._changedProperties=new Map,this._updateState=-5&this._updateState}get updateComplete(){return this._getUpdateComplete()}_getUpdateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._updatePromise}shouldUpdate(e){return!0}update(e){void 0!==this._reflectingProperties&&this._reflectingProperties.size>0&&(this._reflectingProperties.forEach(((e,t)=>this._propertyToAttribute(t,this[t],e))),this._reflectingProperties=void 0),this._markUpdated()}updated(e){}firstUpdated(e){}}yt[_t]=!0; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +const kt=e=>t=>"function"==typeof t?((e,t)=>(window.customElements.define(e,t),t))(e,t):((e,t)=>{const{kind:i,elements:n}=t;return{kind:i,elements:n,finisher(t){window.customElements.define(e,t)}}})(e,t),wt=(e,t)=>"method"===t.kind&&t.descriptor&&!("value"in t.descriptor)?Object.assign(Object.assign({},t),{finisher(i){i.createProperty(t.key,e)}}):{kind:"field",key:Symbol(),placement:"own",descriptor:{},initializer(){"function"==typeof t.initializer&&(this[t.key]=t.initializer.call(this))},finisher(i){i.createProperty(t.key,e)}},$t=(e,t,i)=>{t.constructor.createProperty(i,e)};function St(e){return(t,i)=>void 0!==i?$t(e,t,i):wt(e,t)}function At(e,t){return(i,n)=>{const s={get(){return this.renderRoot.querySelector(e)},enumerable:!0,configurable:!0};if(t){const t=void 0!==n?n:i.key,a="symbol"==typeof t?Symbol():`__${t}`;s.get=function(){return void 0===this[a]&&(this[a]=this.renderRoot.querySelector(e)),this[a]}}return void 0!==n?zt(s,i,n):Ct(s,i)}}const zt=(e,t,i)=>{Object.defineProperty(t,i,e)},Ct=(e,t)=>({kind:"method",placement:"prototype",key:t.key,descriptor:e}) +/** + @license + Copyright (c) 2019 The Polymer Project Authors. All rights reserved. + This code may only be used under the BSD style license found at + http://polymer.github.io/LICENSE.txt The complete set of authors may be found at + http://polymer.github.io/AUTHORS.txt The complete set of contributors may be + found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as + part of the polymer project is also subject to an additional IP rights grant + found at http://polymer.github.io/PATENTS.txt + */,Et=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Pt=Symbol();class qt{constructor(e,t){if(t!==Pt)throw new Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e}get styleSheet(){return void 0===this._styleSheet&&(Et?(this._styleSheet=new CSSStyleSheet,this._styleSheet.replaceSync(this.cssText)):this._styleSheet=null),this._styleSheet}toString(){return this.cssText}}const Mt=(e,...t)=>{const i=t.reduce(((t,i,n)=>t+(e=>{if(e instanceof qt)return e.cssText;if("number"==typeof e)return e;throw new Error(`Value passed to 'css' function must be a 'css' function result: ${e}. Use 'unsafeCSS' to pass non-literal values, but\n take care to ensure page security.`)})(i)+e[n+1]),e[0]);return new qt(i,Pt)}; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +(window.litElementVersions||(window.litElementVersions=[])).push("2.5.1");const Tt={};class Nt extends yt{static getStyles(){return this.styles}static _getUniqueStyles(){if(this.hasOwnProperty(JSCompiler_renameProperty("_styles",this)))return;const e=this.getStyles();if(Array.isArray(e)){const t=(e,i)=>e.reduceRight(((e,i)=>Array.isArray(i)?t(i,e):(e.add(i),e)),i),i=t(e,new Set),n=[];i.forEach((e=>n.unshift(e))),this._styles=n}else this._styles=void 0===e?[]:[e];this._styles=this._styles.map((e=>{if(e instanceof CSSStyleSheet&&!Et){const t=Array.prototype.slice.call(e.cssRules).reduce(((e,t)=>e+t.cssText),"");return new qt(String(t),Pt)}return e}))}initialize(){super.initialize(),this.constructor._getUniqueStyles(),this.renderRoot=this.createRenderRoot(),window.ShadowRoot&&this.renderRoot instanceof window.ShadowRoot&&this.adoptStyles()}createRenderRoot(){return this.attachShadow(this.constructor.shadowRootOptions)}adoptStyles(){const e=this.constructor._styles;0!==e.length&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow?Et?this.renderRoot.adoptedStyleSheets=e.map((e=>e instanceof CSSStyleSheet?e:e.styleSheet)):this._needsShimAdoptedStyleSheets=!0:window.ShadyCSS.ScopingShim.prepareAdoptedCssText(e.map((e=>e.cssText)),this.localName))}connectedCallback(){super.connectedCallback(),this.hasUpdated&&void 0!==window.ShadyCSS&&window.ShadyCSS.styleElement(this)}update(e){const t=this.render();super.update(e),t!==Tt&&this.constructor.render(t,this.renderRoot,{scopeName:this.localName,eventContext:this}),this._needsShimAdoptedStyleSheets&&(this._needsShimAdoptedStyleSheets=!1,this.constructor._styles.forEach((e=>{const t=document.createElement("style");t.textContent=e.cssText,this.renderRoot.appendChild(t)})))}render(){return Tt}}Nt.finalized=!0,Nt.render=(e,t,i)=>{if(!i||"object"!=typeof i||!i.scopeName)throw new Error("The `scopeName` option is required.");const n=i.scopeName,s=tt.has(t),a=ut&&11===t.nodeType&&!!t.host,r=a&&!mt.has(n),o=r?document.createDocumentFragment():t;if(((e,t,i)=>{let n=tt.get(t);void 0===n&&($e(t,t.firstChild),tt.set(t,n=new Fe(Object.assign({templateFactory:Ke},i))),n.appendInto(t)),n.setValue(e),n.commit()})(e,o,Object.assign({templateFactory:pt(n)},i)),r){const e=tt.get(o);tt.delete(o);const i=e.value instanceof Re?e.value.template:void 0;bt(n,o,i),$e(t,t.firstChild),t.appendChild(o),tt.set(t,e)}!s&&a&&window.ShadyCSS.styleElement(t.host)},Nt.shadowRootOptions={mode:"open"}; +/** + * @license + * + * Copyright IBM Corp. 2019, 2022 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ +const{prefix:Ot}=xe,Rt=`\n a[href], area[href], input:not([disabled]):not([tabindex='-1']),\n button:not([disabled]):not([tabindex='-1']),select:not([disabled]):not([tabindex='-1']),\n textarea:not([disabled]):not([tabindex='-1']),\n iframe, object, embed, *[tabindex]:not([tabindex='-1']), *[contenteditable=true],\n ${Ot}-accordion-item,\n ${Ot}-btn,\n ${Ot}-breadcrumb-link,\n ${Ot}-checkbox,\n ${Ot}-code-snippet,\n ${Ot}-combo-box,\n ${Ot}-content-switcher-item,\n ${Ot}-copy-button,\n ${Ot}-table-header-row,\n ${Ot}-table-row,\n ${Ot}-table-toolbar-search,\n ${Ot}-date-picker-input,\n ${Ot}-dropdown,\n ${Ot}-input,\n ${Ot}-link,\n ${Ot}-number-input,\n ${Ot}-modal,\n ${Ot}-modal-close-button,\n ${Ot}-multi-select,\n ${Ot}-inline-notification,\n ${Ot}-toast-notification,\n ${Ot}-overflow-menu,\n ${Ot}-overflow-menu-item,\n ${Ot}-page-sizes-select,\n ${Ot}-pages-select,\n ${Ot}-progress-step,\n ${Ot}-radio-button,\n ${Ot}-search,\n ${Ot}-slider,\n ${Ot}-slider-input,\n ${Ot}-structured-list,\n ${Ot}-tab,\n ${Ot}-filter-tag,\n ${Ot}-textarea,\n ${Ot}-clickable-tile,\n ${Ot}-expandable-tile,\n ${Ot}-radio-tile,\n ${Ot}-selectable-tile,\n ${Ot}-toggle,\n ${Ot}-tooltip,\n ${Ot}-tooltip-definition,\n ${Ot}-tooltip-icon,\n ${Ot}-header-menu,\n ${Ot}-header-menu-button,\n ${Ot}-header-menu-item,\n ${Ot}-header-name,\n ${Ot}-header-nav-item,\n ${Ot}-side-nav-link,\n ${Ot}-side-nav-menu,\n ${Ot}-side-nav-menu-item\n` +/** + * @license + * + * Copyright IBM Corp. 2019, 2022 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */,It=e=>class extends e{focus(){if(this.shadowRoot.delegatesFocus)super.focus();else{const e=this.shadowRoot.querySelector(Rt)||this.querySelector(Rt);e?e.focus():super.focus()}}} +/** + * @license + * + * Copyright IBM Corp. 2019, 2020 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */;var Ut=Mt(["a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{padding:0;border:0;margin:0;font:inherit;font-size:100%;vertical-align:baseline}button,input,select,textarea{border-radius:0;font-family:inherit}input[type=text]::-ms-clear{display:none}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section{display:block}body{line-height:1}sup{vertical-align:super}sub{vertical-align:sub}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote::after,blockquote::before,q::after,q::before{content:\"\"}table{border-collapse:collapse;border-spacing:0}*{box-sizing:border-box}button{margin:0}html{font-size:100%}body{font-weight:400;font-family:'IBM Plex Sans','Helvetica Neue',Arial,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}code{font-family:'IBM Plex Mono',Menlo,'DejaVu Sans Mono','Bitstream Vera Sans Mono',Courier,monospace}strong{font-weight:600}@media screen and (-ms-high-contrast:active){svg{fill:ButtonText}}h1{font-size:var(--cds-productive-heading-06-font-size,2.625rem);font-weight:var(--cds-productive-heading-06-font-weight,300);line-height:var(--cds-productive-heading-06-line-height,1.199);letter-spacing:var(--cds-productive-heading-06-letter-spacing,0)}h2{font-size:var(--cds-productive-heading-05-font-size,2rem);font-weight:var(--cds-productive-heading-05-font-weight,300);line-height:var(--cds-productive-heading-05-line-height,1.25);letter-spacing:var(--cds-productive-heading-05-letter-spacing,0)}h3{font-size:var(--cds-productive-heading-04-font-size,1.75rem);font-weight:var(--cds-productive-heading-04-font-weight,400);line-height:var(--cds-productive-heading-04-line-height,1.28572);letter-spacing:var(--cds-productive-heading-04-letter-spacing,0)}h4{font-size:var(--cds-productive-heading-03-font-size,1.25rem);font-weight:var(--cds-productive-heading-03-font-weight,400);line-height:var(--cds-productive-heading-03-line-height,1.4);letter-spacing:var(--cds-productive-heading-03-letter-spacing,0)}h5{font-size:var(--cds-productive-heading-02-font-size,1rem);font-weight:var(--cds-productive-heading-02-font-weight,600);line-height:var(--cds-productive-heading-02-line-height,1.375);letter-spacing:var(--cds-productive-heading-02-letter-spacing,0)}h6{font-size:var(--cds-productive-heading-01-font-size,.875rem);font-weight:var(--cds-productive-heading-01-font-weight,600);line-height:var(--cds-productive-heading-01-line-height,1.28572);letter-spacing:var(--cds-productive-heading-01-letter-spacing,.16px)}p{font-size:var(--cds-body-long-02-font-size,1rem);font-weight:var(--cds-body-long-02-font-weight,400);line-height:var(--cds-body-long-02-line-height,1.5);letter-spacing:var(--cds-body-long-02-letter-spacing,0)}a{color:#0f62fe}em{font-style:italic}.bx--assistive-text,.bx--visually-hidden{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;border:0;margin:-1px;clip:rect(0,0,0,0);visibility:inherit;white-space:nowrap}.bx--body{box-sizing:border-box;padding:0;border:0;margin:0;font-family:inherit;font-size:100%;vertical-align:baseline;font-size:var(--cds-body-short-01-font-size,.875rem);font-weight:var(--cds-body-short-01-font-weight,400);line-height:var(--cds-body-short-01-line-height,1.28572);letter-spacing:var(--cds-body-short-01-letter-spacing,.16px);background-color:var(--cds-ui-background,#fff);color:var(--cds-text-01,#161616);line-height:1}.bx--body *,.bx--body ::after,.bx--body ::before{box-sizing:inherit}@keyframes skeleton{0%{opacity:.3;transform:scaleX(0);transform-origin:left}20%{opacity:1;transform:scaleX(1);transform-origin:left}28%{transform:scaleX(1);transform-origin:right}51%{transform:scaleX(0);transform-origin:right}58%{transform:scaleX(0);transform-origin:right}82%{transform:scaleX(1);transform-origin:right}83%{transform:scaleX(1);transform-origin:left}96%{transform:scaleX(0);transform-origin:left}100%{opacity:.3;transform:scaleX(0);transform-origin:left}}.bx--text-truncate--end{display:inline-block;overflow:hidden;width:100%;text-overflow:ellipsis;white-space:nowrap}.bx--text-truncate--front{display:inline-block;overflow:hidden;width:100%;direction:rtl;text-overflow:ellipsis;white-space:nowrap}.bx--side-nav,:host(bx-side-nav){position:fixed;z-index:8000;top:0;bottom:0;left:0;overflow:hidden;width:3rem;max-width:16rem;background-color:#fff;color:#525252;transition:width .11s cubic-bezier(.2,0,1,.9);will-change:width}.bx--side-nav--ux,:host(bx-side-nav){top:var(--cds-spacing-09,3rem);width:16rem}@media (max-width:65.98rem){.bx--side-nav--ux,:host(bx-side-nav){width:0}}.bx--side-nav--rail{width:3rem}.bx--side-nav--hidden{width:0}.bx--side-nav--expanded,.bx--side-nav--rail:not(.bx--side-nav--fixed):hover:host(bx-side-nav),.bx--side-nav.bx--side-nav--rail:not(.bx--side-nav--fixed):hover,:host(bx-side-nav[collapse-mode][expanded]),:host(bx-side-nav[expanded]){width:16rem}.bx--side-nav__overlay{position:fixed;top:3rem;left:0;width:0;height:0;background-color:transparent;opacity:0;transition:opacity 240ms cubic-bezier(.2,0,.38,.9),background-color 240ms cubic-bezier(.2,0,.38,.9)}@media (max-width:65.98rem){.bx--side-nav__overlay-active{width:100vw;height:100vh;background-color:var(--cds-overlay-01,rgba(22,22,22,.5));opacity:1;transition:opacity 240ms cubic-bezier(.2,0,.38,.9),background-color 240ms cubic-bezier(.2,0,.38,.9)}}.bx--header~.bx--side-nav,.bx--header~:host(bx-side-nav){top:3rem;height:calc(100% - 48px)}.bx--side-nav--fixed{width:16rem}.bx--side-nav--collapsed{width:16rem;transform:translateX(-16rem)}.bx--side-nav__navigation,:host(bx-side-nav){display:flex;height:100%;flex-direction:column}.bx--side-nav__header{display:flex;width:100%;max-width:100%;height:3rem;border-bottom:1px solid #393939}.bx--side-nav--expanded .bx--side-nav__header,.bx--side-nav--fixed .bx--side-nav__header,.bx--side-nav:hover .bx--side-nav__header,:host(bx-side-nav[expanded]) .bx--side-nav__header,:hover:host(bx-side-nav) .bx--side-nav__header{height:auto}.bx--side-nav--ux .bx--side-nav__header,:host(bx-side-nav) .bx--side-nav__header{height:auto}.bx--side-nav__details{display:flex;min-width:0;flex:1;flex-direction:column;padding-right:1rem;opacity:0;visibility:hidden}.bx--side-nav--expanded .bx--side-nav__details,.bx--side-nav--fixed .bx--side-nav__details,.bx--side-nav:hover .bx--side-nav__details,:host(bx-side-nav[expanded]) .bx--side-nav__details,:hover:host(bx-side-nav) .bx--side-nav__details{visibility:inherit;opacity:1}.bx--side-nav--ux .bx--side-nav__details,:host(bx-side-nav) .bx--side-nav__details{opacity:1;visibility:inherit}.bx--side-nav__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:1rem;font-size:.875rem;font-weight:600;letter-spacing:.1px;-webkit-user-select:none;-moz-user-select:none;user-select:none}.bx--side-nav__select,.bx--side-nav__title{padding-left:.5rem}.bx--side-nav__switcher{position:relative;display:flex;align-items:center;justify-content:space-between}.bx--side-nav__switcher-chevron{position:absolute;top:0;right:.5rem;bottom:0;display:flex;align-items:center;fill:#525252}.bx--side-nav__select{outline:2px solid transparent;outline-offset:-2px;min-width:0;height:2rem;flex:1 1 0%;padding-right:2rem;border:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#161616;border-radius:0;color:#f4f4f4;cursor:pointer;font-size:.75rem;transition:outline 110ms}.bx--side-nav__select:focus{outline:2px solid var(--cds-focus,#0f62fe);outline-offset:-2px}@media screen and (prefers-contrast){.bx--side-nav__select:focus{outline-style:dotted}}.bx--side-nav__footer{width:100%;flex:0 0 3rem;background-color:#fff}.bx--side-nav__toggle{outline:2px solid transparent;outline-offset:-2px;box-sizing:border-box;padding:0;border:0;margin:0;font-family:inherit;font-size:100%;vertical-align:baseline;display:inline-block;padding:0;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:0 0;cursor:pointer;width:100%;height:100%;padding-left:1rem;text-align:left;transition:outline 110ms}.bx--side-nav__toggle *,.bx--side-nav__toggle ::after,.bx--side-nav__toggle ::before{box-sizing:inherit}.bx--side-nav__toggle::-moz-focus-inner{border:0}.bx--side-nav__toggle:focus{outline:2px solid var(--cds-focus,#0f62fe);outline-offset:-2px}@media screen and (prefers-contrast){.bx--side-nav__toggle:focus{outline-style:dotted}}.bx--side-nav__items,:host(bx-side-nav-items){overflow:hidden;flex:1 1 0%;padding:1rem 0 0}.bx--side-nav--expanded .bx--side-nav__items,.bx--side-nav--expanded :host(bx-side-nav-items),.bx--side-nav--fixed .bx--side-nav__items,.bx--side-nav--fixed :host(bx-side-nav-items),.bx--side-nav:hover .bx--side-nav__items,.bx--side-nav:hover :host(bx-side-nav-items),:host(bx-side-nav[expanded]) .bx--side-nav__items,:host(bx-side-nav[expanded]) :host(bx-side-nav-items),:hover:host(bx-side-nav) .bx--side-nav__items,:hover:host(bx-side-nav) :host(bx-side-nav-items){overflow-y:auto}.bx--side-nav--ux .bx--side-nav__items,.bx--side-nav--ux :host(bx-side-nav-items),:host(bx-side-nav) .bx--side-nav__items,:host(bx-side-nav) :host(bx-side-nav-items){overflow-y:auto}.bx--side-nav__item,:host(bx-side-nav-link),:host(bx-side-nav-menu){overflow:hidden;width:auto;height:auto}.bx--side-nav--ux .bx--side-nav__item,.bx--side-nav--ux :host(bx-side-nav-link),.bx--side-nav--ux :host(bx-side-nav-menu),:host(bx-side-nav) .bx--side-nav__item,:host(bx-side-nav) :host(bx-side-nav-link),:host(bx-side-nav) :host(bx-side-nav-menu){width:auto;height:auto}.bx--side-nav .bx--header__menu-title[aria-expanded=true]:hover,.bx--side-nav a.bx--header__menu-item:hover,.bx--side-nav__item:not(.bx--side-nav__item--active):hover .bx--side-nav__item:not(.bx--side-nav__item--active)>.bx--side-nav__submenu:hover,.bx--side-nav__item:not(.bx--side-nav__item--active):hover :not(.bx--side-nav__item--active):host(bx-side-nav-link)>.bx--side-nav__submenu:hover,.bx--side-nav__item:not(.bx--side-nav__item--active):hover :not(.bx--side-nav__item--active):host(bx-side-nav-menu)>.bx--side-nav__submenu:hover,.bx--side-nav__item:not(.bx--side-nav__item--active)>.bx--side-nav__link:hover,.bx--side-nav__menu a.bx--side-nav__link:not(.bx--side-nav__link--current):not([aria-current=page]):hover,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]:hover,:host(bx-side-nav) a.bx--header__menu-item:hover,:not(.bx--side-nav__item--active):host(bx-side-nav-link)>.bx--side-nav__link:hover,:not(.bx--side-nav__item--active):host(bx-side-nav-menu)>.bx--side-nav__link:hover,:not(.bx--side-nav__item--active):hover:host(bx-side-nav-link) .bx--side-nav__item:not(.bx--side-nav__item--active)>.bx--side-nav__submenu:hover,:not(.bx--side-nav__item--active):hover:host(bx-side-nav-link) :not(.bx--side-nav__item--active):host(bx-side-nav-link)>.bx--side-nav__submenu:hover,:not(.bx--side-nav__item--active):hover:host(bx-side-nav-link) :not(.bx--side-nav__item--active):host(bx-side-nav-menu)>.bx--side-nav__submenu:hover,:not(.bx--side-nav__item--active):hover:host(bx-side-nav-menu) .bx--side-nav__item:not(.bx--side-nav__item--active)>.bx--side-nav__submenu:hover,:not(.bx--side-nav__item--active):hover:host(bx-side-nav-menu) :not(.bx--side-nav__item--active):host(bx-side-nav-link)>.bx--side-nav__submenu:hover,:not(.bx--side-nav__item--active):hover:host(bx-side-nav-menu) :not(.bx--side-nav__item--active):host(bx-side-nav-menu)>.bx--side-nav__submenu:hover{background-color:#e5e5e5;color:#161616}.bx--side-nav__item:not(.bx--side-nav__item--active) .bx--side-nav__menu-item>.bx--side-nav__link:hover>span,.bx--side-nav__item:not(.bx--side-nav__item--active) :host(bx-side-nav-menu-item)>.bx--side-nav__link:hover>span,.bx--side-nav__item:not(.bx--side-nav__item--active)>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-link) .bx--side-nav__menu-item>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-link) :host(bx-side-nav-menu-item)>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-link)>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-menu) .bx--side-nav__menu-item>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-menu) :host(bx-side-nav-menu-item)>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-menu)>.bx--side-nav__link:hover>span{color:#161616}.bx--side-nav__item--large{height:3rem}.bx--side-nav__divider,:host(bx-side-nav-divider){height:1px;margin:var(--cds-spacing-03,.5rem) var(--cds-spacing-05,1rem);background-color:#e0e0e0}.bx--side-nav__submenu{box-sizing:border-box;padding:0;border:0;margin:0;font-family:inherit;font-size:100%;vertical-align:baseline;display:inline-block;padding:0;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:0 0;cursor:pointer;width:100%;font-size:var(--cds-productive-heading-01-font-size,.875rem);font-weight:var(--cds-productive-heading-01-font-weight,600);line-height:var(--cds-productive-heading-01-line-height,1.28572);letter-spacing:var(--cds-productive-heading-01-letter-spacing,.16px);outline:2px solid transparent;outline-offset:-2px;display:flex;height:2rem;align-items:center;padding:0 1rem;color:#525252;transition:color 110ms,background-color 110ms,outline 110ms;-webkit-user-select:none;-moz-user-select:none;user-select:none}.bx--side-nav__submenu *,.bx--side-nav__submenu ::after,.bx--side-nav__submenu ::before{box-sizing:inherit}.bx--side-nav__submenu::-moz-focus-inner{border:0}.bx--side-nav__submenu:hover{background-color:#e5e5e5;color:#161616}.bx--side-nav__submenu:focus{outline:2px solid var(--cds-focus,#0f62fe);outline-offset:-2px}@media screen and (prefers-contrast){.bx--side-nav__submenu:focus{outline-style:dotted}}.bx--side-nav__submenu-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}.bx--side-nav__icon.bx--side-nav__submenu-chevron{display:flex;flex:1;justify-content:flex-end}.bx--side-nav__submenu-chevron>svg{width:1rem;height:1rem;transition:transform 110ms}.bx--side-nav__submenu[aria-expanded=true] .bx--side-nav__submenu-chevron>svg{transform:rotate(180deg)}.bx--side-nav__item--large .bx--side-nav__submenu{height:3rem}.bx--side-nav__item--active .bx--side-nav__submenu:hover,:host(bx-side-nav-menu[active]) .bx--side-nav__submenu:hover{background-color:#e5e5e5;color:#161616}.bx--side-nav__item--active .bx--side-nav__submenu[aria-expanded=false],:host(bx-side-nav-menu[active]) .bx--side-nav__submenu[aria-expanded=false]{position:relative;background-color:#e5e5e5;color:#161616}.bx--side-nav__item--active .bx--side-nav__submenu[aria-expanded=false]::before,:host(bx-side-nav-menu[active]) .bx--side-nav__submenu[aria-expanded=false]::before{position:absolute;top:0;bottom:0;left:0;width:4px;background-color:#0f62fe;content:\"\"}.bx--side-nav__item--active .bx--side-nav__submenu-title,:host(bx-side-nav-menu[active]) .bx--side-nav__submenu-title{color:#161616;font-weight:600}.bx--side-nav__menu{display:block;max-height:0;visibility:hidden}.bx--side-nav__submenu[aria-expanded=true]+.bx--side-nav__menu{max-height:93.75rem;visibility:inherit}.bx--side-nav__menu a.bx--side-nav__link{height:2rem;min-height:2rem;padding-left:2rem;font-weight:400}.bx--side-nav__item--icon:host(bx-side-nav-link) a.bx--side-nav__link,.bx--side-nav__item--icon:host(bx-side-nav-menu) a.bx--side-nav__link,.bx--side-nav__item.bx--side-nav__item--icon a.bx--side-nav__link,:host(bx-side-nav-menu):host(bx-side-nav-menu[has-icon]) a.bx--side-nav__link{padding-left:4.5rem}.bx--side-nav__menu a.bx--side-nav__link--current,.bx--side-nav__menu a.bx--side-nav__link[aria-current=page],a.bx--side-nav__link--current{background-color:#e0e0e0}.bx--side-nav__menu a.bx--side-nav__link--current>span,.bx--side-nav__menu a.bx--side-nav__link[aria-current=page]>span,a.bx--side-nav__link--current>span{color:#161616;font-weight:600}.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu,.bx--side-nav a.bx--header__menu-item,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu,:host(bx-side-nav) a.bx--header__menu-item,a.bx--side-nav__link{outline:2px solid transparent;outline-offset:-2px;font-size:var(--cds-productive-heading-01-font-size,.875rem);font-weight:var(--cds-productive-heading-01-font-weight,600);line-height:var(--cds-productive-heading-01-line-height,1.28572);letter-spacing:var(--cds-productive-heading-01-letter-spacing,.16px);position:relative;display:flex;min-height:2rem;align-items:center;padding:0 1rem;text-decoration:none;transition:color 110ms,background-color 110ms,outline 110ms}.bx--side-nav__item--large a.bx--side-nav__link{height:3rem}.bx--side-nav a.bx--header__menu-item .bx--text-truncate-end,:host(bx-side-nav) a.bx--header__menu-item .bx--text-truncate-end,a.bx--side-nav__link>.bx--side-nav__link-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#525252;font-size:.875rem;letter-spacing:.1px;line-height:1.25rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.bx--side-nav a.bx--header__menu-item:focus,:host(bx-side-nav) a.bx--header__menu-item:focus,a.bx--side-nav__link:focus{outline:2px solid var(--cds-focus,#0f62fe);outline-offset:-2px}@media screen and (prefers-contrast){.bx--side-nav a.bx--header__menu-item:focus,:host(bx-side-nav) a.bx--header__menu-item:focus,a.bx--side-nav__link:focus{outline-style:dotted}}a.bx--side-nav__link--current,a.bx--side-nav__link[aria-current=page]{background-color:#e5e5e5;font-weight:600}a.bx--side-nav__link--current .bx--side-nav__link-text,a.bx--side-nav__link[aria-current=page] .bx--side-nav__link-text{color:#161616}a.bx--side-nav__link--current::before,a.bx--side-nav__link[aria-current=page]::before{position:absolute;top:0;bottom:0;left:0;width:4px;background-color:#0f62fe;content:\"\"}.bx--side-nav__icon{display:flex;flex:0 0 1rem;align-items:center;justify-content:center}.bx--side-nav__icon:not(.bx--side-nav__submenu-chevron){margin-right:1.5rem}.bx--side-nav__icon>svg{width:1rem;height:1rem;fill:#525252}.bx--side-nav__icon>svg.bx--side-nav-collapse-icon{display:none}.bx--side-nav--expanded .bx--side-nav__icon>svg.bx--side-nav-expand-icon,:host(bx-side-nav[expanded]) .bx--side-nav__icon>svg.bx--side-nav-expand-icon{display:none}.bx--side-nav--expanded .bx--side-nav__icon>svg.bx--side-nav-collapse-icon,:host(bx-side-nav[expanded]) .bx--side-nav__icon>svg.bx--side-nav-collapse-icon{display:block}.bx--side-nav--fixed .bx--side-nav__submenu,.bx--side-nav--fixed a.bx--side-nav__link{padding-left:1rem}.bx--side-nav--fixed .bx--side-nav__item:not(.bx--side-nav__item--icon) .bx--side-nav__menu a.bx--side-nav__link,.bx--side-nav--fixed :not(.bx--side-nav__item--icon):host(bx-side-nav-link) .bx--side-nav__menu a.bx--side-nav__link,.bx--side-nav--fixed :not(.bx--side-nav__item--icon):host(bx-side-nav-menu) .bx--side-nav__menu a.bx--side-nav__link{padding-left:2rem}@media (max-width:65.98rem){.bx--side-nav .bx--header__nav,:host(bx-side-nav) .bx--header__nav{display:block}}.bx--side-nav__header-navigation{display:none}@media (max-width:65.98rem){.bx--side-nav__header-navigation{position:relative;display:block;margin-bottom:2rem}}.bx--side-nav__header-divider::after{position:absolute;bottom:-1rem;left:1rem;width:calc(100% - 32px);height:.0625rem;background:#e0e0e0;content:\"\"}.bx--side-nav a.bx--header__menu-item,:host(bx-side-nav) a.bx--header__menu-item{justify-content:space-between;color:#525252;white-space:nowrap}.bx--side-nav a.bx--header__menu-item[aria-expanded=true],:host(bx-side-nav) a.bx--header__menu-item[aria-expanded=true]{background-color:transparent}.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu{bottom:inherit;width:100%;padding:0;background-color:transparent;box-shadow:none;transform:none}.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu li,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu li{width:100%}.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu a.bx--header__menu-item,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu a.bx--header__menu-item{padding-left:4.25rem;font-weight:400}.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu a.bx--header__menu-item:hover,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu a.bx--header__menu-item:hover{background-color:#e5e5e5;color:#161616}.bx--side-nav .bx--header__menu a.bx--header__menu-item,:host(bx-side-nav) .bx--header__menu a.bx--header__menu-item{height:inherit}.bx--side-nav .bx--header__menu-arrow,.bx--side-nav a.bx--header__menu-item:focus .bx--header__menu-arrow,.bx--side-nav a.bx--header__menu-item:hover .bx--header__menu-arrow,:host(bx-side-nav) .bx--header__menu-arrow{fill:#525252}@media screen and (-ms-high-contrast:active),(forced-colors:active){.bx--side-nav .bx--header__menu-arrow,.bx--side-nav a.bx--header__menu-item:focus .bx--header__menu-arrow,.bx--side-nav a.bx--header__menu-item:hover .bx--header__menu-arrow,.bx--side-nav__icon>svg,:host(bx-side-nav) .bx--header__menu-arrow{fill:ButtonText}}:host(bx-side-nav){top:0}:host(bx-side-nav[collapse-mode=fixed]){width:16rem}:host(bx-side-nav[collapse-mode=rail]){width:3rem}:host(bx-side-nav[collapse-mode=rail]):hover{width:16rem}:host(bx-side-nav[collapse-mode][usage-mode=header-nav]),:host(bx-side-nav[usage-mode=header-nav]){width:0}@media (max-width:65.98rem){:host(bx-side-nav[collapse-mode][expanded][usage-mode=header-nav]),:host(bx-side-nav[expanded][usage-mode=header-nav]){width:16rem}}:host(bx-side-nav-link){display:block;outline:0;width:auto;height:auto}:host(bx-side-nav-link) .bx--side-nav__icon{color:#525252}:host(bx-side-nav-link) .bx--side-nav__icon[hidden]{display:none}:host(bx-side-nav-divider){display:block}:host(bx-side-nav-menu){display:block;outline:0;width:auto;height:auto}:host(bx-side-nav-menu) .bx--side-nav__icon[hidden]{display:none}:host(bx-side-nav-menu[active]){background-color:#e5e5e5;color:#161616;position:relative}:host(bx-side-nav-menu[active])::before{content:\"\";position:absolute;top:0;bottom:0;left:0;width:4px;background-color:#0f62fe}:host(bx-side-nav-menu[active][expanded]){background-color:inherit;color:inherit;position:inherit}:host(bx-side-nav-menu[active][expanded])::before{content:none}:host(bx-side-nav-menu-item){display:block;outline:0;width:auto;height:auto}:host(bx-side-nav-menu-item) a.bx--side-nav__link{height:2rem;min-height:2rem;padding-left:2rem;font-weight:400}:host(bx-side-nav-menu-item[parent-has-icon]) a.bx--side-nav__link{padding-left:4.5rem}:host(bx-side-nav-item) .bx--side-nav__link:hover,:host(bx-side-nav-menu) .bx--side-nav__submenu:hover,:host(bx-side-nav-menu-item) .bx--side-nav__link:hover{background-color:#e5e5e5;color:#161616}"]);let Ht,jt=e=>e;const{prefix:Lt}=xe;function Vt(e){return Vt=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},Vt(e)}function Bt(){return Bt="undefined"!=typeof Reflect&&Reflect.get?Reflect.get.bind():function(e,t,i){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=Vt(e)););return e}(e,t);if(n){var s=Object.getOwnPropertyDescriptor(n,t);return s.get?s.get.call(arguments.length<3?e:i):s.value}},Bt.apply(this,arguments)} +/** + * @license + * + * Copyright IBM Corp. 2019, 2020 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ue([kt(`${Lt}-side-nav-menu-item`)],(function(e,t){return{F:class extends t{constructor(...t){super(...t),e(this)}},d:[{kind:"field",decorators:[St({type:Boolean,reflect:!0})],key:"active",value:()=>!1},{kind:"field",decorators:[St()],key:"href",value:()=>""},{kind:"field",decorators:[St()],key:"title",value:void 0},{kind:"method",key:"createRenderRoot",value:function(){var e;return this.attachShadow({mode:"open",delegatesFocus:Number((null!==(e=/Safari\/(\d+)/.exec(navigator.userAgent))&&void 0!==e?e:["",0])[1])<=537})}},{kind:"method",key:"shouldUpdate",value:function(e){if(e.has("active")&&this.active){const{selectorMenu:e}=this.constructor,t=this.closest(e);t&&(t.active=!0)}return!0}},{kind:"method",key:"render",value:function(){const{active:e,href:t,title:i}=this,n=rt({[`${Lt}--side-nav__link`]:!0,[`${Lt}--side-nav__link--current`]:e});return it(Ht||(Ht=jt` ${0} `),n,t,Lt,i)}},{kind:"get",static:!0,key:"selectorMenu",value:function(){return`${Lt}-side-nav-menu`}},{kind:"field",static:!0,key:"styles",value:()=>Ut}]}}),It(Nt));var Dt=Mt([".bx--text-truncate--end{display:inline-block;overflow:hidden;width:100%;text-overflow:ellipsis;white-space:nowrap}.bx--text-truncate--front{display:inline-block;overflow:hidden;width:100%;direction:rtl;text-overflow:ellipsis;white-space:nowrap}a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{padding:0;border:0;margin:0;font:inherit;font-size:100%;vertical-align:baseline}button,input,select,textarea{border-radius:0;font-family:inherit}input[type=text]::-ms-clear{display:none}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section{display:block}body{line-height:1}sup{vertical-align:super}sub{vertical-align:sub}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote::after,blockquote::before,q::after,q::before{content:\"\"}table{border-collapse:collapse;border-spacing:0}*{box-sizing:border-box}button{margin:0}html{font-size:100%}body{font-weight:400;font-family:'IBM Plex Sans','Helvetica Neue',Arial,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}code{font-family:'IBM Plex Mono',Menlo,'DejaVu Sans Mono','Bitstream Vera Sans Mono',Courier,monospace}strong{font-weight:600}@media screen and (-ms-high-contrast:active){svg{fill:ButtonText}}h1{font-size:var(--cds-productive-heading-06-font-size,2.625rem);font-weight:var(--cds-productive-heading-06-font-weight,300);line-height:var(--cds-productive-heading-06-line-height,1.199);letter-spacing:var(--cds-productive-heading-06-letter-spacing,0)}h2{font-size:var(--cds-productive-heading-05-font-size,2rem);font-weight:var(--cds-productive-heading-05-font-weight,300);line-height:var(--cds-productive-heading-05-line-height,1.25);letter-spacing:var(--cds-productive-heading-05-letter-spacing,0)}h3{font-size:var(--cds-productive-heading-04-font-size,1.75rem);font-weight:var(--cds-productive-heading-04-font-weight,400);line-height:var(--cds-productive-heading-04-line-height,1.28572);letter-spacing:var(--cds-productive-heading-04-letter-spacing,0)}h4{font-size:var(--cds-productive-heading-03-font-size,1.25rem);font-weight:var(--cds-productive-heading-03-font-weight,400);line-height:var(--cds-productive-heading-03-line-height,1.4);letter-spacing:var(--cds-productive-heading-03-letter-spacing,0)}h5{font-size:var(--cds-productive-heading-02-font-size,1rem);font-weight:var(--cds-productive-heading-02-font-weight,600);line-height:var(--cds-productive-heading-02-line-height,1.375);letter-spacing:var(--cds-productive-heading-02-letter-spacing,0)}h6{font-size:var(--cds-productive-heading-01-font-size,.875rem);font-weight:var(--cds-productive-heading-01-font-weight,600);line-height:var(--cds-productive-heading-01-line-height,1.28572);letter-spacing:var(--cds-productive-heading-01-letter-spacing,.16px)}p{font-size:var(--cds-body-long-02-font-size,1rem);font-weight:var(--cds-body-long-02-font-weight,400);line-height:var(--cds-body-long-02-line-height,1.5);letter-spacing:var(--cds-body-long-02-letter-spacing,0)}a{color:#0f62fe}em{font-style:italic}@keyframes skeleton{0%{opacity:.3;transform:scaleX(0);transform-origin:left}20%{opacity:1;transform:scaleX(1);transform-origin:left}28%{transform:scaleX(1);transform-origin:right}51%{transform:scaleX(0);transform-origin:right}58%{transform:scaleX(0);transform-origin:right}82%{transform:scaleX(1);transform-origin:right}83%{transform:scaleX(1);transform-origin:left}96%{transform:scaleX(0);transform-origin:left}100%{opacity:.3;transform:scaleX(0);transform-origin:left}}.bx--header,:host(bx-header){position:fixed;z-index:8000;top:0;right:0;left:0;display:flex;height:3rem;align-items:center;border-bottom:1px solid #393939;background-color:#161616}.bx--header__action{box-sizing:border-box;padding:0;border:0;margin:0;font-family:inherit;font-size:100%;vertical-align:baseline;display:inline-block;padding:0;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:0 0;cursor:pointer;width:100%;width:3rem;height:3rem;border:.0625rem solid transparent;transition:background-color 110ms,border-color 110ms}.bx--header__action *,.bx--header__action ::after,.bx--header__action ::before{box-sizing:inherit}.bx--header__action::-moz-focus-inner{border:0}.bx--header__action--active>svg.bx--navigation-menu-panel-expand-icon,.bx--header__action>svg.bx--navigation-menu-panel-collapse-icon{display:none}.bx--header__action--active>svg.bx--navigation-menu-panel-collapse-icon{display:inline}.bx--header__action:hover{background-color:#353535}.bx--header__action--active{border-right:1px solid #393939;border-bottom:1px solid #161616;border-left:1px solid #393939}.bx--header__action:focus{border-color:#fff;outline:0}.bx--header__action:active{background-color:#393939}.bx--header__action.bx--btn--icon-only.bx--tooltip__trigger{justify-content:center}.bx--header__action>svg{fill:#fff}.bx--header__menu-trigger>svg{fill:#f4f4f4}.bx--header__menu-trigger:hover{fill:#2c2c2c}.bx--header__menu-toggle{display:flex;align-items:center;justify-content:center}@media (min-width:66rem){.bx--header__menu-toggle__hidden{display:none}}a.bx--header__name{font-size:var(--cds-body-short-01-font-size,.875rem);font-weight:var(--cds-body-short-01-font-weight,400);line-height:var(--cds-body-short-01-line-height,1.28572);letter-spacing:var(--cds-body-short-01-letter-spacing,.16px);display:flex;height:100%;align-items:center;padding:0 2rem 0 1rem;border:.125rem solid transparent;font-weight:600;letter-spacing:.1px;line-height:1.25rem;outline:0;text-decoration:none;transition:border-color 110ms;-webkit-user-select:none;-moz-user-select:none;user-select:none}a.bx--header__name:focus{border-color:#fff}.bx--header__name--prefix{font-weight:400}a.bx--header__name,a.bx--header__name:hover{color:#f4f4f4}.bx--header__menu-toggle:not(.bx--header__menu-toggle__hidden)~.bx--header__name{padding-left:.5rem}.bx--header__nav,:host(bx-header-nav){position:relative;display:none;height:100%;padding-left:1rem}@media (min-width:66rem){.bx--header__nav,:host(bx-header-nav){display:block}}.bx--header__nav::before,:host(bx-header-nav)::before{position:absolute;top:50%;left:0;display:block;width:.0625rem;height:1.5rem;background-color:#393939;content:\"\";transform:translateY(-50%)}.bx--header__menu-bar{display:flex;height:100%;padding:0;margin:0;list-style:none}a.bx--header__menu-item{position:relative;display:flex;height:100%;align-items:center;padding:0 1rem;border:2px solid transparent;color:#c6c6c6;font-size:.875rem;font-weight:400;letter-spacing:0;line-height:1.125rem;text-decoration:none;transition:background-color 110ms,border-color 110ms,color 110ms;-webkit-user-select:none;-moz-user-select:none;user-select:none}a.bx--header__menu-item:hover{background-color:#2c2c2c;color:#f4f4f4}.bx--header__action:active,a.bx--header__menu-item:active{background-color:#393939;color:#f4f4f4}a.bx--header__menu-item:focus{border-color:#fff;color:#f4f4f4;outline:0}a.bx--header__menu-item:active>svg,a.bx--header__menu-item:focus>svg,a.bx--header__menu-item:hover>svg{fill:#f4f4f4}.bx--header__menu-item--current::after,a.bx--header__menu-item[aria-current=page]::after{position:absolute;top:0;right:0;bottom:-2px;left:0;width:100%;border-bottom:3px solid var(--cds-inverse-support-04,#4589ff);content:\"\"}.bx--header__menu-item--current:focus::after,a.bx--header__menu-item[aria-current=page]:focus::after{border:0}a.bx--header__menu-item.bx--header__menu-item--current:focus,a.bx--header__menu-item[aria-current=page]:focus{border:2px solid #fff}.bx--header__submenu,:host(bx-header-menu){position:relative}.bx--header__submenu--current::after{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;border-bottom:3px solid var(--cds-border-interactive,#0f62fe);content:\"\"}.bx--header__submenu--current:focus{border:2px solid var(--cds-focus,#0f62fe)}.bx--header__submenu--current:focus::after{border:0}.bx--header__menu-title[aria-haspopup=true]{position:relative}.bx--header__menu-title[aria-expanded=true]{z-index:8001;background-color:#262626;color:#fff}.bx--header__menu-title[aria-expanded=true]>.bx--header__menu-arrow{transform:rotate(180deg)}.bx--header__menu{display:none;padding:0;margin:0;list-style:none}.bx--header__menu-title[aria-expanded=true]+.bx--header__menu{position:absolute;z-index:8000;bottom:0;left:0;display:flex;width:12.5rem;flex-direction:column;background-color:#262626;box-shadow:0 4px 8px 0 rgba(0,0,0,.5);transform:translateY(100%)}.bx--header__menu-title[aria-expanded=true]+.bx--header__menu .bx--header__menu-item:hover{background-color:#353535}.bx--header__menu-title[aria-expanded=true]+.bx--header__menu .bx--header__menu-item:active{background-color:#393939}.bx--header__menu .bx--header__menu-item{height:3rem}.bx--header__menu .bx--header__menu-item:hover{background-color:#262626;color:#f4f4f4}.bx--header__menu-arrow{margin-left:.5rem;fill:#c6c6c6;transition:transform 110ms,fill 110ms}.bx--header__global{display:flex;height:100%;flex:1 1 0%;justify-content:flex-end}.bx--skip-to-content{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;border:0;margin:-1px;clip:rect(0,0,0,0);visibility:inherit;white-space:nowrap}.bx--skip-to-content:focus{z-index:9999;top:0;left:0;display:flex;width:auto;height:3rem;align-items:center;padding:0 1rem;border:4px solid #0f62fe;background-color:#161616;clip:auto;color:#f4f4f4;outline:0}:host(bx-header-nav) .bx-ce--header__divider{position:absolute;left:0;top:50%;transform:translateY(-50%);height:1.5rem;width:.0625rem;background-color:#393939}:host(bx-header-nav-item){outline:0}:host(bx-header-menu){outline:0}:host(bx-header-menu-item){outline:0}:host(bx-header-menu-item) a.bx--header__menu-item{height:3rem}:host(bx-header-menu-item) a.bx--header__menu-item:hover{background-color:#353535;color:#f4f4f4}:host(bx-header-menu-item) a.bx--header__menu-item:active{background-color:#393939}:host(bx-header-menu-button){display:content;outline:0}@media (min-width:66rem){:host(bx-header-menu-button){display:none}}:host(bx-header-menu-button[collapse-mode=fixed]){display:none}@media (min-width:66rem){:host(bx-header-menu-button[collapse-mode=rail]){display:block}}:host(bx-header-name){display:content;height:100%}"]);let Ft,Xt=e=>e;const{prefix:Wt}=xe;function Gt(e){for(var t=arguments.length,i=new Array(t>1?t-1:0),n=1;n `))}},{kind:"field",static:!0,key:"styles",value:()=>Dt}]}}),Nt);const Qt=/^((document|window|parentRoot|shadowRoot):)?([\w-]+)$/,Jt=e=>{class t extends e{constructor(...e){super(...e),Yt(this,"_handles",new Set)}connectedCallback(){super.connectedCallback();const e=this.constructor._hostListeners;Object.keys(e).forEach((t=>{Object.keys(e[t]).forEach((i=>{var n;const s=Qt.exec(i);if(!s)throw new Error(`Could not parse the event name: ${t}`);const[,,a,r]=s,o={document:this.ownerDocument,window:this.ownerDocument.defaultView,parentRoot:this.getRootNode(),shadowRoot:this.shadowRoot}[a]||this,{options:d}=e[t][i];this._handles.add(Gt(o,null!==(n=this.constructor[r])&&void 0!==n?n:r,this[t],d))}))}))}disconnectedCallback(){this._handles.forEach((e=>{e.release(),this._handles.delete(e)})),super.disconnectedCallback()}}return Yt(t,"_hostListeners",{}),t};function Zt(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,n)}return i}function Kt(e){for(var t=1;t{const s=i._hostListeners;if(!s)throw new Error("The method `@HostListener()` is defined on has to be of a class that has `HostListerMixin`.");s[n]||(s[n]={}),s[n][e]={options:t}},ti=(e,t)=>(i,n)=>void 0!==n?ei(e,t,i.constructor,n):((e,t,i)=>{const{kind:n,key:s,placement:a}=i;if(!("method"===n&&"prototype"===a||"field"===n&&"own"===a))throw new Error("`@HostListener()` must be defined on instance methods, but you may have defined it on static, field, etc.");return Kt(Kt({},i),{},{finisher(i){ei(e,t,i,s)}})})(e,t,i) +/** + * @license + * + * Copyright IBM Corp. 2019, 2022 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */,ii=(e,t,i)=>Array.prototype.forEach.call(e,t,i) +/** + * @license + * + * Copyright IBM Corp. 2020 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */;let ni,si;!function(e){e.FIXED="fixed",e.RAIL="rail",e.RESPONSIVE="responsive"}(ni||(ni={})),function(e){e.REGULAR="",e.HEADER_NAV="header-nav"}(si||(si={}));let ai,ri=e=>e;const{prefix:oi}=xe;ue([kt(`${oi}-side-nav`)],(function(e,t){class i extends t{constructor(...t){super(...t),e(this)}}return{F:i,d:[{kind:"field",key:"_hovered",value:()=>!1},{kind:"field",key:"_hTransition",value:()=>null},{kind:"field",key:"_transitionPromise",value:()=>Promise.resolve()},{kind:"get",key:"_updateAndTransitionPromise",value:function(){return this.updateComplete.then((()=>this._transitionPromise))}},{kind:"method",key:"_cleanHTransition",value:function(){this._hTransition&&(this._hTransition=this._hTransition.release())}},{kind:"field",decorators:[ti("parentRoot:eventButtonToggle")],key:"_handleButtonToggle",value(){return async e=>{var t;(this.expanded=e.detail.active,this.expanded)&&(await this._updateAndTransitionPromise,this.expanded&&(null===(t=this.querySelector(this.constructor.selectorNavItems))||void 0===t||t.focus()))}}},{kind:"method",key:"_updatedSideNavMenuForceCollapsedState",value:function(){const{expanded:e,_hovered:t}=this;ii(this.querySelectorAll(this.constructor.selectorMenu),(i=>{i.forceCollapsed=!e&&!t}))}},{kind:"method",decorators:[ti("mouseover")],key:"_handleMouseover",value:function(){this._hovered=!0,this._updatedSideNavMenuForceCollapsedState()}},{kind:"method",decorators:[ti("mouseout")],key:"_handleMouseout",value:function(){this._hovered=!1,this._updatedSideNavMenuForceCollapsedState()}},{kind:"field",decorators:[St({reflect:!0,attribute:"collapse-mode"})],key:"collapseMode",value:()=>ni.RESPONSIVE},{kind:"field",decorators:[St({type:Boolean,reflect:!0})],key:"expanded",value:()=>!1},{kind:"field",decorators:[St({reflect:!0,attribute:"usage-mode"})],key:"usageMode",value:()=>si.REGULAR},{kind:"method",key:"connectedCallback",value:function(){this.hasAttribute("role")||this.setAttribute("role","navigation"),Bt(Vt(i.prototype),"connectedCallback",this).call(this)}},{kind:"method",key:"disconnectedCallback",value:function(){this._cleanHTransition(),Bt(Vt(i.prototype),"disconnectedCallback",this).call(this)}},{kind:"method",key:"shouldUpdate",value:function(e){return e.has("expanded")&&(this._transitionPromise=new Promise((e=>{this._cleanHTransition(),this._hTransition=Gt(this,"transitionend",(()=>{this._cleanHTransition(),e()}))}))),!0}},{kind:"method",key:"updated",value:function(e){if(e.has("collapseMode")||e.has("usageMode")){const{collapseMode:e,usageMode:t}=this;e!==ni.FIXED&&e!==ni.RAIL||t!==si.HEADER_NAV||console.warn("Fixed/rail modes of side nav cannot be used with header nav mode.")}const t=this.getRootNode();e.has("collapseMode")&&ii(t.querySelectorAll(this.constructor.selectorButtonToggle),(e=>{e.collapseMode=this.collapseMode})),e.has("expanded")&&(this._updatedSideNavMenuForceCollapsedState(),ii(t.querySelectorAll(this.constructor.selectorButtonToggle),(e=>{e.active=this.expanded}))),e.has("usageMode")&&ii(t.querySelectorAll(this.constructor.selectorButtonToggle),(e=>{e.usageMode=this.usageMode}))}},{kind:"method",key:"render",value:function(){return it(ai||(ai=ri` `))}},{kind:"get",static:!0,key:"selectorButtonToggle",value:function(){return`${oi}-header-menu-button`}},{kind:"get",static:!0,key:"selectorNavItems",value:function(){return`${oi}-side-nav-menu, ${oi}-side-nav-menu-item, ${oi}-side-nav-link`}},{kind:"get",static:!0,key:"selectorMenu",value:function(){return`${oi}-side-nav-menu`}},{kind:"get",static:!0,key:"eventButtonToggle",value:function(){return`${oi}-header-menu-button-toggled`}},{kind:"field",static:!0,key:"styles",value:()=>Ut}]}}),Jt(Nt));const{prefix:di}=xe;ue([kt(`${di}-side-nav-divider`)],(function(e,t){class i extends t{constructor(...t){super(...t),e(this)}}return{F:i,d:[{kind:"method",key:"connectedCallback",value:function(){this.hasAttribute("role")||this.setAttribute("role","separator"),Bt(Vt(i.prototype),"connectedCallback",this).call(this)}},{kind:"field",static:!0,key:"styles",value:()=>Ut}]}}),Nt); +/** + * @license + * Copyright (c) 2018 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +const li=new WeakMap,ci=ye((e=>t=>{const i=li.get(t);if(void 0===e&&t instanceof De){if(void 0!==i||!li.has(t)){const e=t.committer.name;t.committer.element.removeAttribute(e)}}else e!==i&&t.setValue(e);li.set(t,e)}));let hi,ui,pi=e=>e;const{prefix:vi}=xe;ue([kt(`${vi}-header-name`)],(function(e,t){return{F:class extends t{constructor(...t){super(...t),e(this)}},d:[{kind:"field",decorators:[St()],key:"href",value:void 0},{kind:"field",decorators:[St()],key:"prefix",value:void 0},{kind:"method",key:"createRenderRoot",value:function(){var e;return this.attachShadow({mode:"open",delegatesFocus:Number((null!==(e=/Safari\/(\d+)/.exec(navigator.userAgent))&&void 0!==e?e:["",0])[1])<=537})}},{kind:"method",key:"render",value:function(){const{href:e,prefix:t}=this,i=t?it(hi||(hi=pi` ${0} `),vi,t):void 0;return it(ui||(ui=pi` ${0}  `),vi,ci(e),i)}},{kind:"field",static:!0,key:"styles",value:()=>Dt}]}}),It(Nt));let mi,bi=e=>e;const{prefix:fi}=xe;ue([kt(`${fi}-header-nav`)],(function(e,t){class i extends t{constructor(...t){super(...t),e(this)}}return{F:i,d:[{kind:"field",decorators:[St({attribute:"menu-bar-label"})],key:"menuBarLabel",value:void 0},{kind:"method",key:"connectedCallback",value:function(){this.hasAttribute("role")||this.setAttribute("role","navigation"),Bt(Vt(i.prototype),"connectedCallback",this).call(this)}},{kind:"method",key:"render",value:function(){const{menuBarLabel:e}=this;return it(mi||(mi=bi`
`),fi,fi,e)}},{kind:"field",static:!0,key:"styles",value:()=>Dt}]}}),Nt);let gi,xi=e=>e;const{prefix:_i}=xe;ue([kt(`${_i}-side-nav-link`)],(function(e,t){class i extends t{constructor(...t){super(...t),e(this)}}return{F:i,d:[{kind:"field",decorators:[At("#title-icon-container")],key:"_titleIconContainerNode",value:void 0},{kind:"method",key:"_handleSlotChangeTitleIcon",value:function({target:e}){var t;null===(t=this._titleIconContainerNode)||void 0===t||t.toggleAttribute("hidden",0===e.assignedNodes().length)}},{kind:"field",decorators:[St({type:Boolean,reflect:!0})],key:"active",value:()=>!1},{kind:"field",decorators:[St()],key:"href",value:()=>""},{kind:"field",decorators:[St()],key:"title",value:void 0},{kind:"method",key:"createRenderRoot",value:function(){var e;return this.attachShadow({mode:"open",delegatesFocus:Number((null!==(e=/Safari\/(\d+)/.exec(navigator.userAgent))&&void 0!==e?e:["",0])[1])<=537})}},{kind:"method",key:"connectedCallback",value:function(){this.hasAttribute("role")||this.setAttribute("role","listitem"),Bt(Vt(i.prototype),"connectedCallback",this).call(this)}},{kind:"method",key:"render",value:function(){const{active:e,href:t,title:i,_handleSlotChangeTitleIcon:n}=this,s=rt({[`${_i}--side-nav__link`]:!0,[`${_i}--side-nav__link--current`]:e});return it(gi||(gi=xi` ${0} `),s,t,_i,n,_i,i)}},{kind:"field",static:!0,key:"styles",value:()=>Ut}]}}),It(Nt)); +/** + * @license + * + * Copyright IBM Corp. 2019, 2022 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ +const yi=new WeakMap,ki=ye((e=>t=>{if(!(t instanceof Ge)||".."!==t.committer.name||t.committer.parts.length>1)throw new Error("The `spread` directive must be used in with `...` name and must be the only part in the attribute.");const{committer:i}=t,{element:n}=i,s=yi.get(t);s&&Object.keys(s).forEach((t=>{t in e||n.removeAttribute(t)})),Object.keys(e).forEach((t=>{const i=e[t];s&&Object.is(i,s[t])||void 0===i||n.setAttribute(t,i)})),yi.set(t,e)}));let wi,$i=e=>e;const{prefix:Si}=xe;let Ai,zi=ue([kt(`${Si}-header-menu`)],(function(e,t){class i extends t{constructor(...t){super(...t),e(this)}}return{F:i,d:[{kind:"field",decorators:[At("a")],key:"_trigger",value:void 0},{kind:"method",key:"_handleClick",value:function(){this._handleUserInitiatedToggle()}},{kind:"method",key:"_handleKeydownTrigger",value:function({key:e}){"Esc"!==e&&"Escape"!==e||this._handleUserInitiatedToggle(!1)}},{kind:"method",key:"_handleUserInitiatedToggle",value:function(e=!this.expanded){this.expanded=e,e||this._trigger.focus()}},{kind:"method",decorators:[ti("focusout")],key:"_handleBlur",value:function({relatedTarget:e}){this.contains(e)||(this.expanded=!1)}},{kind:"field",decorators:[St({type:Boolean,reflect:!0})],key:"expanded",value:()=>!1},{kind:"field",decorators:[St({attribute:"trigger-content"})],key:"triggerContent",value:()=>""},{kind:"field",decorators:[St({attribute:"menu-label"})],key:"menuLabel",value:void 0},{kind:"method",key:"createRenderRoot",value:function(){return this.attachShadow({mode:"open",delegatesFocus:!0})}},{kind:"method",key:"connectedCallback",value:function(){this.hasAttribute("role")||this.setAttribute("role","listitem"),Bt(Vt(i.prototype),"connectedCallback",this).call(this)}},{kind:"method",key:"updated",value:function(e){if(e.has("expanded")){const{selectorItem:e}=this.constructor,{expanded:t}=this;ii(this.querySelectorAll(e),(e=>{e.tabIndex=t?0:-1}))}}},{kind:"method",key:"render",value:function(){const{expanded:e,triggerContent:t,menuLabel:i,_handleClick:n,_handleKeydownTrigger:s}=this;return it(wi||(wi=$i` ${0}${0}
`),Si,Si,String(Boolean(e)),n,s,t,(({children:e,...t}={})=>nt``)({part:"trigger-icon",class:`${Si}--header__menu-arrow`}),Si,ci(i))}},{kind:"get",static:!0,key:"selectorItem",value:function(){return`${Si}-header-menu-item`}},{kind:"field",static:!0,key:"styles",value:()=>Dt}]}}),Jt(It(Nt))),Ci=e=>e;const{prefix:Ei}=xe;let Pi=ue([kt(`${Ei}-header-nav-item`)],(function(e,t){return{F:class extends t{constructor(...t){super(...t),e(this)}},d:[{kind:"field",decorators:[St()],key:"href",value:void 0},{kind:"field",decorators:[St()],key:"title",value:void 0},{kind:"field",decorators:[St({reflect:!0})],key:"role",value:()=>"listitem"},{kind:"method",key:"createRenderRoot",value:function(){var e;return this.attachShadow({mode:"open",delegatesFocus:Number((null!==(e=/Safari\/(\d+)/.exec(navigator.userAgent))&&void 0!==e?e:["",0])[1])<=537})}},{kind:"method",key:"render",value:function(){const{href:e,title:t}=this;return it(Ai||(Ai=Ci` ${0} `),Ei,ci(e),Ei,t)}},{kind:"field",static:!0,key:"styles",value:()=>Dt}]}}),It(Nt));const{prefix:qi}=xe;ue([kt(`${qi}-header-menu-item`)],(function(e,t){return{F:class extends t{constructor(...t){super(...t),e(this)}},d:[]}}),Pi);let Mi,Ti=e=>e;const{prefix:Ni}=xe;ue([kt(`${Ni}-side-nav-items`)],(function(e,t){class i extends t{constructor(...t){super(...t),e(this)}}return{F:i,d:[{kind:"method",key:"connectedCallback",value:function(){this.hasAttribute("role")||this.setAttribute("role","list"),Bt(Vt(i.prototype),"connectedCallback",this).call(this)}},{kind:"method",key:"render",value:function(){return it(Mi||(Mi=Ti` `))}},{kind:"field",static:!0,key:"styles",value:()=>Ut}]}}),Nt); +/** + * @license + * + * Copyright IBM Corp. 2019, 2020 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ +const Oi=({children:e,...t}={})=>nt`` +/** + * @license + * + * Copyright IBM Corp. 2019, 2020 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */,Ri=({children:e,...t}={})=>nt`` +/** + * @license + * + * Copyright IBM Corp. 2019, 2022 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */;let Ii,Ui=e=>e;const{prefix:Hi}=xe;let ji=ue([kt(`${Hi}-header-menu-button`)],(function(e,t){return{F:class extends t{constructor(...t){super(...t),e(this)}},d:[{kind:"method",key:"_handleClick",value:function(){const e=!this.active;this.active=e,this.dispatchEvent(new CustomEvent(this.constructor.eventToggle,{bubbles:!0,cancelable:!0,composed:!0,detail:{active:e}}))}},{kind:"field",decorators:[St({type:Boolean,reflect:!0})],key:"active",value:()=>!1},{kind:"field",decorators:[St({attribute:"button-label-active"})],key:"buttonLabelActive",value:()=>"Close navigation menu"},{kind:"field",decorators:[St({attribute:"button-label-inactive"})],key:"buttonLabelInactive",value:()=>"Open navigation menu"},{kind:"field",decorators:[St({reflect:!0,attribute:"collapse-mode"})],key:"collapseMode",value:()=>ni.RESPONSIVE},{kind:"field",decorators:[St({type:Boolean,reflect:!0})],key:"disabled",value:()=>!1},{kind:"field",decorators:[St({reflect:!0,attribute:"usage-mode"})],key:"usageMode",value:()=>si.REGULAR},{kind:"method",key:"createRenderRoot",value:function(){var e;return this.attachShadow({mode:"open",delegatesFocus:Number((null!==(e=/Safari\/(\d+)/.exec(navigator.userAgent))&&void 0!==e?e:["",0])[1])<=537})}},{kind:"method",key:"render",value:function(){const{active:e,buttonLabelActive:t,buttonLabelInactive:i,disabled:n,_handleClick:s}=this,a=e?t:i,r=rt({[`${Hi}--header__action`]:!0,[`${Hi}--header__menu-trigger`]:!0,[`${Hi}--header__menu-toggle`]:!0,[`${Hi}--header__action--active`]:e});return it(Ii||(Ii=Ui` `),r,n,ci(null!=(o=a)?o:void 0),s,(e?Oi:Ri)({slot:"toggle-icon"}));var o}},{kind:"get",static:!0,key:"eventToggle",value:function(){return`${Hi}-header-menu-button-toggled`}},{kind:"field",static:!0,key:"styles",value:()=>Dt}]}}),It(Nt)); +/** + * @license + * + * Copyright IBM Corp. 2019, 2020 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */let Li,Vi=e=>e;const{prefix:Bi}=xe;ue([kt(`${Bi}-side-nav-menu`)],(function(e,t){class i extends t{constructor(...t){super(...t),e(this)}}return{F:i,d:[{kind:"field",key:"_hasIcon",value:()=>!1},{kind:"field",decorators:[At("#title-icon-container")],key:"_titleIconContainerNode",value:void 0},{kind:"method",key:"_handleUserInitiatedToggle",value:function(e=!this.expanded){const{eventBeforeToggle:t,eventToggle:i}=this.constructor,n={bubbles:!0,cancelable:!0,composed:!0,detail:{expanded:e}};this.dispatchEvent(new CustomEvent(t,n))&&(this.expanded=e,this.dispatchEvent(new CustomEvent(i,n)))}},{kind:"method",key:"_handleClickExpando",value:function(){this._handleUserInitiatedToggle()}},{kind:"method",key:"_handleSlotChange",value:function({target:e}){const{_hasIcon:t}=this;ii(e.assignedNodes(),(e=>{e.nodeType===Node.ELEMENT_NODE&&e.toggleAttribute(this.constructor.attribItemHasIcon,t)}))}},{kind:"method",key:"_handleSlotChangeTitleIcon",value:function({target:e}){var t;const i=this.constructor,n=e.assignedNodes().length>0;this._hasIcon=n,null===(t=this._titleIconContainerNode)||void 0===t||t.toggleAttribute("hidden",!n),ii(this.querySelectorAll(i.selectorItem),(e=>{e.toggleAttribute(i.attribItemHasIcon,n)}))}},{kind:"field",decorators:[St({type:Boolean,reflect:!0})],key:"active",value:()=>!1},{kind:"field",decorators:[St({type:Boolean,reflect:!0})],key:"expanded",value:()=>!1},{kind:"field",decorators:[St({type:Boolean,reflect:!0,attribute:"force-collapsed"})],key:"forceCollapsed",value:()=>!1},{kind:"field",decorators:[St()],key:"title",value:()=>""},{kind:"method",key:"createRenderRoot",value:function(){var e;return this.attachShadow({mode:"open",delegatesFocus:Number((null!==(e=/Safari\/(\d+)/.exec(navigator.userAgent))&&void 0!==e?e:["",0])[1])<=537})}},{kind:"method",key:"connectedCallback",value:function(){this.hasAttribute("role")||this.setAttribute("role","listitem"),Bt(Vt(i.prototype),"connectedCallback",this).call(this)}},{kind:"method",key:"updated",value:function(e){if(e.has("expanded")){const{selectorItem:e}=this.constructor,{expanded:t}=this;ii(this.querySelectorAll(e),(e=>{e.tabIndex=t?0:-1}))}}},{kind:"method",key:"render",value:function(){const{expanded:e,forceCollapsed:t,title:i,_handleClickExpando:n,_handleSlotChange:s,_handleSlotChangeTitleIcon:a}=this;return it(Li||(Li=Vi`
`),String(Boolean(e&&!t)),Bi,n,Bi,a,Bi,i,Bi,Bi,Bi,(({children:e,...t}={})=>nt``)({part:"expando-icon"}),Bi,s)}},{kind:"field",static:!0,key:"attribItemHasIcon",value:()=>"parent-has-icon"},{kind:"get",static:!0,key:"selectorItem",value:function(){return`${Bi}-side-nav-menu-item`}},{kind:"get",static:!0,key:"eventBeforeToggle",value:function(){return`${Bi}-side-nav-menu-beingtoggled`}},{kind:"get",static:!0,key:"eventToggle",value:function(){return`${Bi}-side-nav-menu-toggled`}},{kind:"field",static:!0,key:"styles",value:()=>Ut}]}}),It(Nt));const Di=((e,...t)=>{const i=1===e.length?e[0]:t.reduce(((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1]),e[0]);return new o(i,e,a)})`.bx--text-truncate--end{display:inline-block;overflow:hidden;width:100%;text-overflow:ellipsis;white-space:nowrap}.bx--text-truncate--front{display:inline-block;overflow:hidden;width:100%;direction:rtl;text-overflow:ellipsis;white-space:nowrap}html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{padding:0;border:0;margin:0;font:inherit;font-size:100%;vertical-align:baseline}button,select,input,textarea{border-radius:0;font-family:inherit}input[type=text]::-ms-clear{display:none}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section{display:block}body{line-height:1}sup{vertical-align:super}sub{vertical-align:sub}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote::before,blockquote::after,q::before,q::after{content:""}table{border-collapse:collapse;border-spacing:0}*{box-sizing:border-box}button{margin:0}html{font-size:100%}body{font-weight:400;font-family:'IBM Plex Sans','Helvetica Neue',Arial,sans-serif;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}code{font-family:'IBM Plex Mono','Menlo','DejaVu Sans Mono','Bitstream Vera Sans Mono',Courier,monospace}strong{font-weight:600}@media screen and (-ms-high-contrast:active){svg{fill:ButtonText}}h1{font-size:2rem;font-weight:300;line-height:1.199;letter-spacing:0}h2{font-size:2rem;font-weight:400;line-height:1.25;letter-spacing:0}h3{font-size:1.75rem;font-weight:400;line-height:1.29;letter-spacing:0}h4{font-size:1.25rem;font-weight:400;line-height:1.4;letter-spacing:0}h5{font-size:1rem;font-weight:600;line-height:1.375;letter-spacing:0}h6{font-size:.875rem;font-weight:600;line-height:1.29;letter-spacing:.16px}p{font-size:1rem;font-weight:400;line-height:1.5;letter-spacing:0}a{color:#0f62fe}em{font-style:italic}@keyframes skeleton{0%{opacity:.3;transform:scaleX(0);transform-origin:left}20%{opacity:1;transform:scaleX(1);transform-origin:left}28%{transform:scaleX(1);transform-origin:right}51%{transform:scaleX(0);transform-origin:right}58%{transform:scaleX(0);transform-origin:right}82%{transform:scaleX(1);transform-origin:right}83%{transform:scaleX(1);transform-origin:left}96%{transform:scaleX(0);transform-origin:left}100%{opacity:.3;transform:scaleX(0);transform-origin:left}}.bx--header,:host(bx-header){position:fixed;z-index:8000;top:0;right:0;left:0;display:flex;height:3rem;align-items:center;border-bottom:1px solid #393939;background-color:#161616}.bx--header__action{box-sizing:border-box;padding:0;border:0;margin:0;font-family:inherit;font-size:100%;vertical-align:baseline;display:inline-block;padding:0;border:0;appearance:none;background:0;cursor:pointer;width:100%;width:3rem;height:3rem;border:.0625rem solid transparent;transition:background-color 110ms,border-color 110ms}.bx--header__action *,.bx--header__action *::before,.bx--header__action *::after{box-sizing:inherit}.bx--header__action::-moz-focus-inner{border:0}.bx--header__action>svg.bx--navigation-menu-panel-collapse-icon,.bx--header__action--active>svg.bx--navigation-menu-panel-expand-icon{display:none}.bx--header__action--active>svg.bx--navigation-menu-panel-collapse-icon{display:inline}.bx--header__action:hover{background-color:#353535}.bx--header__action--active{border-right:1px solid #393939;border-bottom:1px solid #161616;border-left:1px solid #393939}.bx--header__action:focus{border-color:#fff;outline:0}.bx--header__action:active{background-color:#393939}.bx--header__action.bx--btn--icon-only.bx--tooltip__trigger{justify-content:center}.bx--header__action>svg{fill:#fff}.bx--header__menu-trigger>svg{fill:#f4f4f4}.bx--header__menu-trigger:hover{fill:#2c2c2c}.bx--header__menu-toggle{display:flex;align-items:center;justify-content:center}@media(min-width:66rem){.bx--header__menu-toggle__hidden{display:none}}a.bx--header__name{font-size:.875rem;font-weight:400;line-height:1.29;letter-spacing:.16px;display:flex;height:100%;align-items:center;padding:0 2rem 0 1rem;border:.125rem solid transparent;font-weight:600;letter-spacing:.1px;line-height:1.25rem;outline:0;text-decoration:none;transition:border-color 110ms;user-select:none}a.bx--header__name:focus{border-color:#fff}.bx--header__name--prefix{font-weight:400}a.bx--header__name,a.bx--header__name:hover{color:#f4f4f4}.bx--header__menu-toggle:not(.bx--header__menu-toggle__hidden) ~ .bx--header__name{padding-left:.5rem}.bx--header__nav,:host(bx-header-nav){position:relative;display:none;height:100%;padding-left:1rem}@media(min-width:66rem){.bx--header__nav,:host(bx-header-nav){display:block}}.bx--header__nav::before{position:absolute;top:50%;left:0;display:block;width:.0625rem;height:1.5rem;background-color:#393939;content:"";transform:translateY(-50%)}.bx--header__menu-bar{display:flex;height:100%;padding:0;margin:0;list-style:none}a.bx--header__menu-item{position:relative;display:flex;height:100%;align-items:center;padding:0 1rem;border:2px solid transparent;color:#c6c6c6;font-size:.875rem;font-weight:400;letter-spacing:0;line-height:1.125rem;text-decoration:none;transition:background-color 110ms,border-color 110ms,color 110ms;user-select:none}a.bx--header__menu-item:hover{background-color:#2c2c2c;color:#f4f4f4}.bx--header__action:active,a.bx--header__menu-item:active{background-color:#393939;color:#f4f4f4}a.bx--header__menu-item:focus{border-color:#fff;color:#f4f4f4;outline:0}a.bx--header__menu-item:hover>svg,a.bx--header__menu-item:active>svg,a.bx--header__menu-item:focus>svg{fill:#f4f4f4}a.bx--header__menu-item[aria-current=page]::after,.bx--header__menu-item--current::after{position:absolute;top:0;right:0;bottom:-2px;left:0;width:100%;border-bottom:3px solid #4589ff;content:""}a.bx--header__menu-item[aria-current=page]:focus::after,.bx--header__menu-item--current:focus::after{border:0}a.bx--header__menu-item[aria-current=page]:focus,a.bx--header__menu-item.bx--header__menu-item--current:focus{border:2px solid #fff}.bx--header__submenu,:host(bx-header-menu),:host(qiskit-header-menu-mega),:host(qiskit-header-menu){position:relative}.bx--header__submenu--current::after{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;border-bottom:3px solid #0f62fe;content:""}.bx--header__submenu--current:focus{border:2px solid #0f62fe}.bx--header__submenu--current:focus::after{border:0}.bx--header__menu-title[aria-haspopup=true]{position:relative}.bx--header__menu-title[aria-expanded=true]{z-index:8001;background-color:#262626;color:#fff}.bx--header__menu-title[aria-expanded=true]>.bx--header__menu-arrow{transform:rotate(180deg)}.bx--header__menu{display:none;padding:0;margin:0;list-style:none}.bx--header__menu-title[aria-expanded=true]+.bx--header__menu{position:absolute;z-index:8000;bottom:0;left:0;display:flex;width:12.5rem;flex-direction:column;background-color:#262626;box-shadow:0 4px 8px 0 rgba(0,0,0,0.5);transform:translateY(100%)}.bx--header__menu-title[aria-expanded=true]+.bx--header__menu .bx--header__menu-item:hover{background-color:#353535}.bx--header__menu-title[aria-expanded=true]+.bx--header__menu .bx--header__menu-item:active{background-color:#393939}.bx--header__menu .bx--header__menu-item{height:3rem}.bx--header__menu .bx--header__menu-item:hover{background-color:#262626;color:#f4f4f4}.bx--header__menu-arrow{margin-left:.5rem;fill:#c6c6c6;transition:transform 110ms,fill 110ms}.bx--header__global{display:flex;height:100%;flex:1 1 0%;justify-content:flex-end}.bx--skip-to-content{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;border:0;margin:-1px;clip:rect(0,0,0,0);visibility:inherit;white-space:nowrap}.bx--skip-to-content:focus{z-index:9999;top:0;left:0;display:flex;width:auto;height:3rem;align-items:center;padding:0 1rem;border:4px solid #0f62fe;background-color:#161616;clip:auto;color:#f4f4f4;outline:0}:host(bx-header-nav) .bx-ce--header__divider{position:absolute;left:0;top:50%;transform:translateY(-50%);height:1.5rem;width:.0625rem;background-color:#393939}:host(bx-header-nav-item){outline:0}:host(bx-header-menu),:host(qiskit-header-menu-mega),:host(qiskit-header-menu){outline:0}:host(bx-header-menu-item){outline:0}:host(bx-header-menu-item) a.bx--header__menu-item{height:3rem}:host(bx-header-menu-item) a.bx--header__menu-item:hover{background-color:#353535;color:#f4f4f4}:host(bx-header-menu-item) a.bx--header__menu-item:active{background-color:#393939}:host(bx-header-menu-button),:host(qiskit-header-menu-button){display:content;outline:0}@media(min-width:66rem){:host(bx-header-menu-button),:host(qiskit-header-menu-button){display:none}}:host(bx-header-menu-button[collapse-mode=fixed]){display:none}@media(min-width:66rem){:host(bx-header-menu-button[collapse-mode=rail]){display:block}}:host(bx-header-name){display:content;height:100%}.bx--assistive-text,.bx--visually-hidden{position:absolute;overflow:hidden;width:1px;height:1px;padding:0;border:0;margin:-1px;clip:rect(0,0,0,0);visibility:inherit;white-space:nowrap}.bx--body{box-sizing:border-box;padding:0;border:0;margin:0;font-family:inherit;font-size:100%;vertical-align:baseline;font-size:.875rem;font-weight:400;line-height:1.29;letter-spacing:.16px;background-color:#fff;color:#161616;line-height:1}.bx--body *,.bx--body *::before,.bx--body *::after{box-sizing:inherit}.bx--side-nav,:host(bx-side-nav){position:fixed;z-index:8000;top:0;bottom:0;left:0;overflow:hidden;width:3rem;max-width:16rem;background-color:#fff;color:#525252;transition:width .11s cubic-bezier(0.2,0,1,0.9);will-change:width}.bx--side-nav--ux,:host(bx-side-nav){top:3rem;width:16rem}@media(max-width:65.98rem){.bx--side-nav--ux,:host(bx-side-nav){width:0}}.bx--side-nav--rail{width:3rem}.bx--side-nav--hidden{width:0}.bx--side-nav.bx--side-nav--rail:not(.bx--side-nav--fixed):hover,.bx--side-nav--expanded,:host(bx-side-nav[expanded]),:host(bx-side-nav[collapse-mode][expanded]){width:16rem}.bx--side-nav__overlay{position:fixed;top:3rem;left:0;width:0;height:0;background-color:transparent;opacity:0;transition:opacity 240ms cubic-bezier(0.2,0,0.38,0.9),background-color 240ms cubic-bezier(0.2,0,0.38,0.9)}@media(max-width:65.98rem){.bx--side-nav__overlay-active{width:100vw;height:100vh;background-color:rgba(22,22,22,0.5);opacity:1;transition:opacity 240ms cubic-bezier(0.2,0,0.38,0.9),background-color 240ms cubic-bezier(0.2,0,0.38,0.9)}}.bx--header ~ .bx--side-nav,.bx--header ~ :host(bx-side-nav),:host(bx-header) ~ .bx--side-nav,:host(bx-header) ~ :host(bx-side-nav){top:3rem;height:calc(100% - 48px)}.bx--side-nav--fixed{width:16rem}.bx--side-nav--collapsed{width:16rem;transform:translateX(-16rem)}.bx--side-nav__navigation,:host(bx-side-nav){display:flex;height:100%;flex-direction:column}.bx--side-nav__header{display:flex;width:100%;max-width:100%;height:3rem;border-bottom:1px solid #393939}.bx--side-nav:hover .bx--side-nav__header,.bx--side-nav--fixed .bx--side-nav__header,.bx--side-nav--expanded .bx--side-nav__header,:host(bx-side-nav[expanded]) .bx--side-nav__header{height:auto}.bx--side-nav--ux .bx--side-nav__header,:host(bx-side-nav) .bx--side-nav__header{height:auto}.bx--side-nav__details{display:flex;min-width:0;flex:1;flex-direction:column;padding-right:1rem;opacity:0;visibility:hidden}.bx--side-nav:hover .bx--side-nav__details,.bx--side-nav--fixed .bx--side-nav__details,.bx--side-nav--expanded .bx--side-nav__details,:host(bx-side-nav[expanded]) .bx--side-nav__details{visibility:inherit;opacity:1}.bx--side-nav--ux .bx--side-nav__details,:host(bx-side-nav) .bx--side-nav__details{opacity:1;visibility:inherit}.bx--side-nav__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:1rem;font-size:.875rem;font-weight:600;letter-spacing:.1px;user-select:none}.bx--side-nav__title,.bx--side-nav__select{padding-left:.5rem}.bx--side-nav__switcher{position:relative;display:flex;align-items:center;justify-content:space-between}.bx--side-nav__switcher-chevron{position:absolute;top:0;right:.5rem;bottom:0;display:flex;align-items:center;fill:#525252}.bx--side-nav__select{outline:2px solid transparent;outline-offset:-2px;min-width:0;height:2rem;flex:1 1 0%;padding-right:2rem;border:0;appearance:none;background-color:#161616;border-radius:0;color:#f4f4f4;cursor:pointer;font-size:.75rem;transition:outline 110ms}.bx--side-nav__select:focus{outline:2px solid #0f62fe;outline-offset:-2px}@media screen and (prefers-contrast){.bx--side-nav__select:focus{outline-style:dotted}}.bx--side-nav__footer{width:100%;flex:0 0 3rem;background-color:#fff}.bx--side-nav__toggle{outline:2px solid transparent;outline-offset:-2px;box-sizing:border-box;padding:0;border:0;margin:0;font-family:inherit;font-size:100%;vertical-align:baseline;display:inline-block;padding:0;border:0;appearance:none;background:0;cursor:pointer;width:100%;height:100%;padding-left:1rem;text-align:left;transition:outline 110ms}.bx--side-nav__toggle *,.bx--side-nav__toggle *::before,.bx--side-nav__toggle *::after{box-sizing:inherit}.bx--side-nav__toggle::-moz-focus-inner{border:0}.bx--side-nav__toggle:focus{outline:2px solid #0f62fe;outline-offset:-2px}@media screen and (prefers-contrast){.bx--side-nav__toggle:focus{outline-style:dotted}}.bx--side-nav__items,:host(bx-side-nav-items){overflow:hidden;flex:1 1 0%;padding:1rem 0 0}.bx--side-nav:hover .bx--side-nav__items,.bx--side-nav:hover :host(bx-side-nav-items),.bx--side-nav--fixed .bx--side-nav__items,.bx--side-nav--fixed :host(bx-side-nav-items),.bx--side-nav--expanded .bx--side-nav__items,.bx--side-nav--expanded :host(bx-side-nav-items),:host(bx-side-nav[expanded]) .bx--side-nav__items,:host(bx-side-nav[expanded]) :host(bx-side-nav-items){overflow-y:auto}.bx--side-nav--ux .bx--side-nav__items,.bx--side-nav--ux :host(bx-side-nav-items),:host(bx-side-nav) .bx--side-nav__items,:host(bx-side-nav) :host(bx-side-nav-items){overflow-y:auto}.bx--side-nav__item,:host(bx-side-nav-menu),:host(bx-side-nav-link){overflow:hidden;width:auto;height:auto}.bx--side-nav--ux .bx--side-nav__item,.bx--side-nav--ux :host(bx-side-nav-menu),.bx--side-nav--ux :host(bx-side-nav-link),:host(bx-side-nav) .bx--side-nav__item,:host(bx-side-nav) :host(bx-side-nav-menu),:host(bx-side-nav) :host(bx-side-nav-link){width:auto;height:auto}.bx--side-nav__item:not(.bx--side-nav__item--active):hover .bx--side-nav__item:not(.bx--side-nav__item--active)>.bx--side-nav__submenu:hover,.bx--side-nav__item:not(.bx--side-nav__item--active):hover :not(.bx--side-nav__item--active):host(bx-side-nav-menu)>.bx--side-nav__submenu:hover,.bx--side-nav__item:not(.bx--side-nav__item--active):hover :not(.bx--side-nav__item--active):host(bx-side-nav-link)>.bx--side-nav__submenu:hover,.bx--side-nav__item:not(.bx--side-nav__item--active)>.bx--side-nav__link:hover,:not(.bx--side-nav__item--active):host(bx-side-nav-menu)>.bx--side-nav__link:hover,:not(.bx--side-nav__item--active):host(bx-side-nav-link)>.bx--side-nav__link:hover,.bx--side-nav__menu a.bx--side-nav__link:not(.bx--side-nav__link--current):not([aria-current=page]):hover,.bx--side-nav a.bx--header__menu-item:hover,:host(bx-side-nav) a.bx--header__menu-item:hover,.bx--side-nav .bx--header__menu-title[aria-expanded=true]:hover,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]:hover{background-color:#e5e5e5;color:#161616}.bx--side-nav__item:not(.bx--side-nav__item--active)>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-menu)>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-link)>.bx--side-nav__link:hover>span,.bx--side-nav__item:not(.bx--side-nav__item--active) .bx--side-nav__menu-item>.bx--side-nav__link:hover>span,.bx--side-nav__item:not(.bx--side-nav__item--active) :host(bx-side-nav-menu-item)>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-menu) .bx--side-nav__menu-item>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-menu) :host(bx-side-nav-menu-item)>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-link) .bx--side-nav__menu-item>.bx--side-nav__link:hover>span,:not(.bx--side-nav__item--active):host(bx-side-nav-link) :host(bx-side-nav-menu-item)>.bx--side-nav__link:hover>span{color:#161616}.bx--side-nav__item--large{height:3rem}.bx--side-nav__divider,:host(bx-side-nav-divider){height:1px;margin:.5rem 1rem;background-color:#e0e0e0}.bx--side-nav__submenu{box-sizing:border-box;padding:0;border:0;margin:0;font-family:inherit;font-size:100%;vertical-align:baseline;display:inline-block;padding:0;border:0;appearance:none;background:0;cursor:pointer;width:100%;font-size:.875rem;font-weight:600;line-height:1.29;letter-spacing:.16px;outline:2px solid transparent;outline-offset:-2px;display:flex;height:2rem;align-items:center;padding:0 1rem;color:#525252;transition:color 110ms,background-color 110ms,outline 110ms;user-select:none}.bx--side-nav__submenu *,.bx--side-nav__submenu *::before,.bx--side-nav__submenu *::after{box-sizing:inherit}.bx--side-nav__submenu::-moz-focus-inner{border:0}.bx--side-nav__submenu:hover{background-color:#e5e5e5;color:#161616}.bx--side-nav__submenu:focus{outline:2px solid #0f62fe;outline-offset:-2px}@media screen and (prefers-contrast){.bx--side-nav__submenu:focus{outline-style:dotted}}.bx--side-nav__submenu-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}.bx--side-nav__icon.bx--side-nav__submenu-chevron{display:flex;flex:1;justify-content:flex-end}.bx--side-nav__submenu-chevron>svg{width:1rem;height:1rem;transition:transform 110ms}.bx--side-nav__submenu[aria-expanded=true] .bx--side-nav__submenu-chevron>svg{transform:rotate(180deg)}.bx--side-nav__item--large .bx--side-nav__submenu{height:3rem}.bx--side-nav__item--active .bx--side-nav__submenu:hover,:host(bx-side-nav-menu[active]) .bx--side-nav__submenu:hover{background-color:#e5e5e5;color:#161616}.bx--side-nav__item--active .bx--side-nav__submenu[aria-expanded=false],:host(bx-side-nav-menu[active]) .bx--side-nav__submenu[aria-expanded=false]{position:relative;background-color:#e5e5e5;color:#161616}.bx--side-nav__item--active .bx--side-nav__submenu[aria-expanded=false]::before,:host(bx-side-nav-menu[active]) .bx--side-nav__submenu[aria-expanded=false]::before{position:absolute;top:0;bottom:0;left:0;width:4px;background-color:#0f62fe;content:""}.bx--side-nav__item--active .bx--side-nav__submenu-title,:host(bx-side-nav-menu[active]) .bx--side-nav__submenu-title{color:#161616;font-weight:600}.bx--side-nav__menu{display:block;max-height:0;visibility:hidden}.bx--side-nav__submenu[aria-expanded=true]+.bx--side-nav__menu{max-height:93.75rem;visibility:inherit}.bx--side-nav__menu a.bx--side-nav__link{height:2rem;min-height:2rem;padding-left:2rem;font-weight:400}.bx--side-nav__item.bx--side-nav__item--icon a.bx--side-nav__link{padding-left:4.5rem}.bx--side-nav__menu a.bx--side-nav__link--current,.bx--side-nav__menu a.bx--side-nav__link[aria-current=page],a.bx--side-nav__link--current{background-color:#e0e0e0}.bx--side-nav__menu a.bx--side-nav__link--current>span,.bx--side-nav__menu a.bx--side-nav__link[aria-current=page]>span,a.bx--side-nav__link--current>span{color:#161616;font-weight:600}a.bx--side-nav__link,.bx--side-nav a.bx--header__menu-item,:host(bx-side-nav) a.bx--header__menu-item,.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu{outline:2px solid transparent;outline-offset:-2px;font-size:.875rem;font-weight:600;line-height:1.29;letter-spacing:.16px;position:relative;display:flex;min-height:2rem;align-items:center;padding:0 1rem;text-decoration:none;transition:color 110ms,background-color 110ms,outline 110ms}.bx--side-nav__item--large a.bx--side-nav__link{height:3rem}a.bx--side-nav__link>.bx--side-nav__link-text,.bx--side-nav a.bx--header__menu-item .bx--text-truncate-end,:host(bx-side-nav) a.bx--header__menu-item .bx--text-truncate-end{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#525252;font-size:.875rem;letter-spacing:.1px;line-height:1.25rem;user-select:none}a.bx--side-nav__link:focus,.bx--side-nav a.bx--header__menu-item:focus,:host(bx-side-nav) a.bx--header__menu-item:focus{outline:2px solid #0f62fe;outline-offset:-2px}@media screen and (prefers-contrast){a.bx--side-nav__link:focus,.bx--side-nav a.bx--header__menu-item:focus,:host(bx-side-nav) a.bx--header__menu-item:focus{outline-style:dotted}}a.bx--side-nav__link[aria-current=page],a.bx--side-nav__link--current{background-color:#e5e5e5;font-weight:600}a.bx--side-nav__link[aria-current=page] .bx--side-nav__link-text,a.bx--side-nav__link--current .bx--side-nav__link-text{color:#161616}a.bx--side-nav__link[aria-current=page]::before,a.bx--side-nav__link--current::before{position:absolute;top:0;bottom:0;left:0;width:4px;background-color:#0f62fe;content:""}.bx--side-nav__icon{display:flex;flex:0 0 1rem;align-items:center;justify-content:center}.bx--side-nav__icon:not(.bx--side-nav__submenu-chevron){margin-right:1.5rem}.bx--side-nav__icon>svg{width:1rem;height:1rem;fill:#525252}.bx--side-nav__icon>svg.bx--side-nav-collapse-icon{display:none}.bx--side-nav--expanded .bx--side-nav__icon>svg.bx--side-nav-expand-icon,:host(bx-side-nav[expanded]) .bx--side-nav__icon>svg.bx--side-nav-expand-icon{display:none}.bx--side-nav--expanded .bx--side-nav__icon>svg.bx--side-nav-collapse-icon,:host(bx-side-nav[expanded]) .bx--side-nav__icon>svg.bx--side-nav-collapse-icon{display:block}.bx--side-nav--fixed a.bx--side-nav__link,.bx--side-nav--fixed .bx--side-nav__submenu{padding-left:1rem}.bx--side-nav--fixed .bx--side-nav__item:not(.bx--side-nav__item--icon) .bx--side-nav__menu a.bx--side-nav__link,.bx--side-nav--fixed :not(.bx--side-nav__item--icon):host(bx-side-nav-menu) .bx--side-nav__menu a.bx--side-nav__link,.bx--side-nav--fixed :not(.bx--side-nav__item--icon):host(bx-side-nav-link) .bx--side-nav__menu a.bx--side-nav__link{padding-left:2rem}@media(max-width:65.98rem){.bx--side-nav .bx--header__nav,:host(bx-side-nav) .bx--header__nav,.bx--side-nav:host(bx-header-nav),:host(bx-side-nav):host(bx-header-nav){display:block}}.bx--side-nav__header-navigation{display:none}@media(max-width:65.98rem){.bx--side-nav__header-navigation{position:relative;display:block;margin-bottom:2rem}}.bx--side-nav__header-divider::after{position:absolute;bottom:-1rem;left:1rem;width:calc(100% - 32px);height:.0625rem;background:#e0e0e0;content:""}.bx--side-nav a.bx--header__menu-item,:host(bx-side-nav) a.bx--header__menu-item{justify-content:space-between;color:#525252;white-space:nowrap}.bx--side-nav a.bx--header__menu-item[aria-expanded=true],:host(bx-side-nav) a.bx--header__menu-item[aria-expanded=true]{background-color:transparent}.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu{bottom:inherit;width:100%;padding:0;background-color:transparent;box-shadow:none;transform:none}.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu li,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu li{width:100%}.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu a.bx--header__menu-item,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu a.bx--header__menu-item{padding-left:4.25rem;font-weight:400}.bx--side-nav .bx--header__menu-title[aria-expanded=true]+.bx--header__menu a.bx--header__menu-item:hover,:host(bx-side-nav) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu a.bx--header__menu-item:hover{background-color:#e5e5e5;color:#161616}.bx--side-nav .bx--header__menu a.bx--header__menu-item,:host(bx-side-nav) .bx--header__menu a.bx--header__menu-item{height:inherit}.bx--side-nav a.bx--header__menu-item:hover .bx--header__menu-arrow,.bx--side-nav a.bx--header__menu-item:focus .bx--header__menu-arrow,.bx--side-nav .bx--header__menu-arrow,:host(bx-side-nav) .bx--header__menu-arrow{fill:#525252}@media screen and (-ms-high-contrast:active),(forced-colors:active){.bx--side-nav__icon>svg,.bx--side-nav a.bx--header__menu-item:hover .bx--header__menu-arrow,.bx--side-nav a.bx--header__menu-item:focus .bx--header__menu-arrow,.bx--side-nav .bx--header__menu-arrow,:host(bx-side-nav) .bx--header__menu-arrow{fill:ButtonText}}:host(bx-side-nav){top:0}:host(bx-side-nav[collapse-mode=fixed]){width:16rem}:host(bx-side-nav[collapse-mode=rail]){width:3rem}:host(bx-side-nav[collapse-mode=rail]):hover{width:16rem}:host(bx-side-nav[usage-mode=header-nav]),:host(bx-side-nav[collapse-mode][usage-mode=header-nav]){width:0}@media(max-width:65.98rem){:host(bx-side-nav[expanded][usage-mode=header-nav]),:host(bx-side-nav[collapse-mode][expanded][usage-mode=header-nav]){width:16rem}}:host(bx-side-nav-link){display:block;outline:0;width:auto;height:auto}:host(bx-side-nav-link) .bx--side-nav__icon{color:#525252}:host(bx-side-nav-link) .bx--side-nav__icon[hidden]{display:none}:host(bx-side-nav-divider){display:block}:host(bx-side-nav-menu){display:block;outline:0;width:auto;height:auto}:host(bx-side-nav-menu) .bx--side-nav__icon[hidden]{display:none}:host(bx-side-nav-menu[active]){background-color:#e5e5e5;color:#161616;position:relative}:host(bx-side-nav-menu[active])::before{content:"";position:absolute;top:0;bottom:0;left:0;width:4px;background-color:#0f62fe}:host(bx-side-nav-menu[active][expanded]){background-color:inherit;color:inherit;position:inherit}:host(bx-side-nav-menu[active][expanded])::before{content:none}:host(bx-side-nav-menu-item){display:block;outline:0;width:auto;height:auto}:host(bx-side-nav-menu-item) a.bx--side-nav__link{height:2rem;min-height:2rem;padding-left:2rem;font-weight:400}:host(bx-side-nav-menu-item[parent-has-icon]) a.bx--side-nav__link{padding-left:4.5rem}:host(bx-side-nav-item) .bx--side-nav__link:hover,:host(bx-side-nav-menu) .bx--side-nav__submenu:hover,:host(bx-side-nav-menu-item) .bx--side-nav__link:hover{background-color:#e5e5e5;color:#161616}:host{--cds-interactive-01:#0f62fe;--cds-interactive-02:#393939;--cds-interactive-03:#0f62fe;--cds-interactive-04:#0f62fe;--cds-ui-background:#fff;--cds-ui-01:#f4f4f4;--cds-ui-02:#fff;--cds-ui-03:#e0e0e0;--cds-ui-04:#8d8d8d;--cds-ui-05:#161616;--cds-text-01:#161616;--cds-text-02:#525252;--cds-text-03:#a8a8a8;--cds-text-04:#fff;--cds-text-05:#6f6f6f;--cds-text-error:#da1e28;--cds-icon-01:#161616;--cds-icon-02:#525252;--cds-icon-03:#fff;--cds-link-01:#0f62fe;--cds-link-02:#0043ce;--cds-inverse-link:#78a9ff;--cds-field-01:#f4f4f4;--cds-field-02:#fff;--cds-inverse-01:#fff;--cds-inverse-02:#393939;--cds-support-01:#da1e28;--cds-support-02:#198038;--cds-support-03:#f1c21b;--cds-support-04:#0043ce;--cds-inverse-support-01:#fa4d56;--cds-inverse-support-02:#42be65;--cds-inverse-support-03:#f1c21b;--cds-inverse-support-04:#4589ff;--cds-overlay-01:rgba(22,22,22,0.5);--cds-danger-01:#da1e28;--cds-danger-02:#da1e28;--cds-focus:#0f62fe;--cds-inverse-focus-ui:#fff;--cds-hover-primary:#0353e9;--cds-active-primary:#002d9c;--cds-hover-primary-text:#0043ce;--cds-hover-secondary:#4c4c4c;--cds-active-secondary:#6f6f6f;--cds-hover-tertiary:#0353e9;--cds-active-tertiary:#002d9c;--cds-hover-ui:#e5e5e5;--cds-hover-light-ui:#e5e5e5;--cds-hover-selected-ui:#cacaca;--cds-active-ui:#c6c6c6;--cds-active-light-ui:#c6c6c6;--cds-selected-ui:#e0e0e0;--cds-selected-light-ui:#e0e0e0;--cds-inverse-hover-ui:#4c4c4c;--cds-hover-danger:#b81921;--cds-active-danger:#750e13;--cds-hover-row:#e5e5e5;--cds-visited-link:#8a3ffc;--cds-disabled-01:#f4f4f4;--cds-disabled-02:#c6c6c6;--cds-disabled-03:#8d8d8d;--cds-highlight:#d0e2ff;--cds-decorative-01:#e0e0e0;--cds-button-separator:#e0e0e0;--cds-skeleton-01:#e5e5e5;--cds-skeleton-02:#c6c6c6;--cds-background:#fff;--cds-layer:#f4f4f4;--cds-layer-accent:#e0e0e0;--cds-layer-accent-hover:#d1d1d1;--cds-layer-accent-active:#a8a8a8;--cds-field:#f4f4f4;--cds-background-inverse:#393939;--cds-background-brand:#0f62fe;--cds-interactive:#0f62fe;--cds-border-subtle:#e0e0e0;--cds-border-strong:#8d8d8d;--cds-border-inverse:#161616;--cds-border-interactive:#0f62fe;--cds-text-primary:#161616;--cds-text-secondary:#525252;--cds-text-placeholder:#a8a8a8;--cds-text-helper:#6f6f6f;--cds-text-on-color:#fff;--cds-text-inverse:#fff;--cds-link-primary:#0f62fe;--cds-link-secondary:#0043ce;--cds-link-visited:#8a3ffc;--cds-link-inverse:#78a9ff;--cds-icon-primary:#161616;--cds-icon-secondary:#525252;--cds-icon-on-color:#fff;--cds-icon-inverse:#fff;--cds-support-error:#da1e28;--cds-support-success:#198038;--cds-support-warning:#f1c21b;--cds-support-info:#0043ce;--cds-support-error-inverse:#fa4d56;--cds-support-success-inverse:#42be65;--cds-support-warning-inverse:#f1c21b;--cds-support-info-inverse:#4589ff;--cds-overlay:rgba(22,22,22,0.5);--cds-toggle-off:#8d8d8d;--cds-shadow:rgba(0,0,0,0.3);--cds-button-primary:#0f62fe;--cds-button-secondary:#393939;--cds-button-tertiary:#0f62fe;--cds-button-danger-primary:#da1e28;--cds-button-danger-secondary:#da1e28;--cds-background-active:#c6c6c6;--cds-layer-active:#c6c6c6;--cds-button-danger-active:#750e13;--cds-button-primary-active:#002d9c;--cds-button-secondary-active:#6f6f6f;--cds-button-tertiary-active:#002d9c;--cds-focus-inset:#fff;--cds-focus-inverse:#fff;--cds-background-hover:#e5e5e5;--cds-layer-hover:#e5e5e5;--cds-field-hover:#e5e5e5;--cds-background-inverse-hover:#4c4c4c;--cds-link-primary-hover:#0043ce;--cds-button-danger-hover:#b81921;--cds-button-primary-hover:#0353e9;--cds-button-secondary-hover:#4c4c4c;--cds-button-tertiary-hover:#0353e9;--cds-background-selected:#e0e0e0;--cds-background-selected-hover:#cacaca;--cds-layer-selected:#e0e0e0;--cds-layer-selected-hover:#cacaca;--cds-layer-selected-inverse:#161616;--cds-border-subtle-selected:#c6c6c6;--cds-border-disabled:#f4f4f4;--cds-text-disabled:#c6c6c6;--cds-button-disabled:#c6c6c6;--cds-icon-disabled:#c6c6c6;--cds-text-on-color-disabled:#8d8d8d;--cds-icon-on-color-disabled:#8d8d8d;--cds-layer-selected-disabled:#8d8d8d;--cds-skeleton-background:#e5e5e5;--cds-skeleton-element:#c6c6c6;--cds-brand-01:#0f62fe;--cds-brand-02:#393939;--cds-brand-03:#0f62fe;--cds-active-01:#c6c6c6;--cds-hover-field:#e5e5e5;--cds-danger:#da1e28;--cds-caption-01-font-size:.75rem;--cds-caption-01-font-weight:400;--cds-caption-01-line-height:1.33333;--cds-caption-01-letter-spacing:.32px;--cds-caption-02-font-size:.875rem;--cds-caption-02-font-weight:400;--cds-caption-02-line-height:1.28572;--cds-caption-02-letter-spacing:.32px;--cds-label-01-font-size:.75rem;--cds-label-01-font-weight:400;--cds-label-01-line-height:1.33333;--cds-label-01-letter-spacing:.32px;--cds-label-02-font-size:.875rem;--cds-label-02-font-weight:400;--cds-label-02-line-height:1.28572;--cds-label-02-letter-spacing:.16px;--cds-helper-text-01-font-size:.75rem;--cds-helper-text-01-line-height:1.33333;--cds-helper-text-01-letter-spacing:.32px;--cds-helper-text-02-font-size:.875rem;--cds-helper-text-02-line-height:1.28572;--cds-helper-text-02-letter-spacing:.16px;--cds-body-short-01-font-size:.875rem;--cds-body-short-01-font-weight:400;--cds-body-short-01-line-height:1.28572;--cds-body-short-01-letter-spacing:.16px;--cds-body-long-01-font-size:.875rem;--cds-body-long-01-font-weight:400;--cds-body-long-01-line-height:1.42857;--cds-body-long-01-letter-spacing:.16px;--cds-body-short-02-font-size:1rem;--cds-body-short-02-font-weight:400;--cds-body-short-02-line-height:1.375;--cds-body-short-02-letter-spacing:0;--cds-body-long-02-font-size:1rem;--cds-body-long-02-font-weight:400;--cds-body-long-02-line-height:1.5;--cds-body-long-02-letter-spacing:0;--cds-code-01-font-family:'IBM Plex Mono','Menlo','DejaVu Sans Mono','Bitstream Vera Sans Mono',Courier,monospace;--cds-code-01-font-size:.75rem;--cds-code-01-font-weight:400;--cds-code-01-line-height:1.33333;--cds-code-01-letter-spacing:.32px;--cds-code-02-font-family:'IBM Plex Mono','Menlo','DejaVu Sans Mono','Bitstream Vera Sans Mono',Courier,monospace;--cds-code-02-font-size:.875rem;--cds-code-02-font-weight:400;--cds-code-02-line-height:1.42857;--cds-code-02-letter-spacing:.32px;--cds-heading-01-font-size:.875rem;--cds-heading-01-font-weight:600;--cds-heading-01-line-height:1.42857;--cds-heading-01-letter-spacing:.16px;--cds-productive-heading-01-font-size:.875rem;--cds-productive-heading-01-font-weight:600;--cds-productive-heading-01-line-height:1.28572;--cds-productive-heading-01-letter-spacing:.16px;--cds-heading-02-font-size:1rem;--cds-heading-02-font-weight:600;--cds-heading-02-line-height:1.5;--cds-heading-02-letter-spacing:0;--cds-productive-heading-02-font-size:1rem;--cds-productive-heading-02-font-weight:600;--cds-productive-heading-02-line-height:1.375;--cds-productive-heading-02-letter-spacing:0;--cds-productive-heading-03-font-size:1.25rem;--cds-productive-heading-03-font-weight:400;--cds-productive-heading-03-line-height:1.4;--cds-productive-heading-03-letter-spacing:0;--cds-productive-heading-04-font-size:1.75rem;--cds-productive-heading-04-font-weight:400;--cds-productive-heading-04-line-height:1.28572;--cds-productive-heading-04-letter-spacing:0;--cds-productive-heading-05-font-size:2rem;--cds-productive-heading-05-font-weight:300;--cds-productive-heading-05-line-height:1.25;--cds-productive-heading-05-letter-spacing:0;--cds-productive-heading-06-font-size:2.625rem;--cds-productive-heading-06-font-weight:300;--cds-productive-heading-06-line-height:1.199;--cds-productive-heading-06-letter-spacing:0;--cds-productive-heading-07-font-size:3.375rem;--cds-productive-heading-07-font-weight:300;--cds-productive-heading-07-line-height:1.199;--cds-productive-heading-07-letter-spacing:0;--cds-expressive-heading-01-font-size:.875rem;--cds-expressive-heading-01-font-weight:600;--cds-expressive-heading-01-line-height:1.25;--cds-expressive-heading-01-letter-spacing:.16px;--cds-expressive-heading-02-font-size:1rem;--cds-expressive-heading-02-font-weight:600;--cds-expressive-heading-02-line-height:1.5;--cds-expressive-heading-02-letter-spacing:0;--cds-expressive-heading-03-font-size:1.25rem;--cds-expressive-heading-03-font-weight:400;--cds-expressive-heading-03-line-height:1.4;--cds-expressive-heading-03-letter-spacing:0;--cds-expressive-heading-04-font-size:1.75rem;--cds-expressive-heading-04-font-weight:400;--cds-expressive-heading-04-line-height:1.28572;--cds-expressive-heading-04-letter-spacing:0;--cds-expressive-heading-05-font-size:2rem;--cds-expressive-heading-05-font-weight:300;--cds-expressive-heading-05-line-height:1.25;--cds-expressive-heading-05-letter-spacing:0;--cds-expressive-heading-06-font-size:2rem;--cds-expressive-heading-06-font-weight:600;--cds-expressive-heading-06-line-height:1.25;--cds-expressive-heading-06-letter-spacing:0;--cds-expressive-paragraph-01-font-size:1.5rem;--cds-expressive-paragraph-01-font-weight:300;--cds-expressive-paragraph-01-line-height:1.334;--cds-expressive-paragraph-01-letter-spacing:0;--cds-quotation-01-font-family:'IBM Plex Serif','Georgia',Times,serif;--cds-quotation-01-font-size:1.25rem;--cds-quotation-01-font-weight:400;--cds-quotation-01-line-height:1.3;--cds-quotation-01-letter-spacing:0;--cds-quotation-02-font-family:'IBM Plex Serif','Georgia',Times,serif;--cds-quotation-02-font-size:2rem;--cds-quotation-02-font-weight:300;--cds-quotation-02-line-height:1.25;--cds-quotation-02-letter-spacing:0;--cds-display-01-font-size:2.625rem;--cds-display-01-font-weight:300;--cds-display-01-line-height:1.19;--cds-display-01-letter-spacing:0;--cds-display-02-font-size:2.625rem;--cds-display-02-font-weight:600;--cds-display-02-line-height:1.19;--cds-display-02-letter-spacing:0;--cds-display-03-font-size:2.625rem;--cds-display-03-font-weight:300;--cds-display-03-line-height:1.19;--cds-display-03-letter-spacing:0;--cds-display-04-font-size:2.625rem;--cds-display-04-font-weight:300;--cds-display-04-line-height:1.19;--cds-display-04-letter-spacing:0;--cds-legal-01-font-size:.75rem;--cds-legal-01-font-weight:400;--cds-legal-01-line-height:1.33333;--cds-legal-01-letter-spacing:.32px;--cds-legal-02-font-size:.875rem;--cds-legal-02-font-weight:400;--cds-legal-02-line-height:1.28572;--cds-legal-02-letter-spacing:.16px;--cds-body-compact-01-font-size:.875rem;--cds-body-compact-01-font-weight:400;--cds-body-compact-01-line-height:1.28572;--cds-body-compact-01-letter-spacing:.16px;--cds-body-compact-02-font-size:1rem;--cds-body-compact-02-font-weight:400;--cds-body-compact-02-line-height:1.375;--cds-body-compact-02-letter-spacing:0;--cds-body-01-font-size:.875rem;--cds-body-01-font-weight:400;--cds-body-01-line-height:1.42857;--cds-body-01-letter-spacing:.16px;--cds-body-02-font-size:1rem;--cds-body-02-font-weight:400;--cds-body-02-line-height:1.5;--cds-body-02-letter-spacing:0;--cds-heading-compact-01-font-size:.875rem;--cds-heading-compact-01-font-weight:600;--cds-heading-compact-01-line-height:1.28572;--cds-heading-compact-01-letter-spacing:.16px;--cds-heading-compact-02-font-size:1rem;--cds-heading-compact-02-font-weight:600;--cds-heading-compact-02-line-height:1.375;--cds-heading-compact-02-letter-spacing:0;--cds-heading-03-font-size:1.25rem;--cds-heading-03-font-weight:400;--cds-heading-03-line-height:1.4;--cds-heading-03-letter-spacing:0;--cds-heading-04-font-size:1.75rem;--cds-heading-04-font-weight:400;--cds-heading-04-line-height:1.28572;--cds-heading-04-letter-spacing:0;--cds-heading-05-font-size:2rem;--cds-heading-05-font-weight:300;--cds-heading-05-line-height:1.25;--cds-heading-05-letter-spacing:0;--cds-heading-06-font-size:2.625rem;--cds-heading-06-font-weight:300;--cds-heading-06-line-height:1.199;--cds-heading-06-letter-spacing:0;--cds-heading-07-font-size:3.375rem;--cds-heading-07-font-weight:300;--cds-heading-07-line-height:1.199;--cds-heading-07-letter-spacing:0;--cds-fluid-heading-03-font-size:1.25rem;--cds-fluid-heading-03-font-weight:400;--cds-fluid-heading-03-line-height:1.4;--cds-fluid-heading-03-letter-spacing:0;--cds-fluid-heading-04-font-size:1.75rem;--cds-fluid-heading-04-font-weight:400;--cds-fluid-heading-04-line-height:1.28572;--cds-fluid-heading-04-letter-spacing:0;--cds-fluid-heading-05-font-size:2rem;--cds-fluid-heading-05-font-weight:300;--cds-fluid-heading-05-line-height:1.25;--cds-fluid-heading-05-letter-spacing:0;--cds-fluid-heading-06-font-size:2rem;--cds-fluid-heading-06-font-weight:600;--cds-fluid-heading-06-line-height:1.25;--cds-fluid-heading-06-letter-spacing:0;--cds-fluid-paragraph-01-font-size:1.5rem;--cds-fluid-paragraph-01-font-weight:300;--cds-fluid-paragraph-01-line-height:1.334;--cds-fluid-paragraph-01-letter-spacing:0;--cds-fluid-quotation-01-font-family:'IBM Plex Serif','Georgia',Times,serif;--cds-fluid-quotation-01-font-size:1.25rem;--cds-fluid-quotation-01-font-weight:400;--cds-fluid-quotation-01-line-height:1.3;--cds-fluid-quotation-01-letter-spacing:0;--cds-fluid-quotation-02-font-family:'IBM Plex Serif','Georgia',Times,serif;--cds-fluid-quotation-02-font-size:2rem;--cds-fluid-quotation-02-font-weight:300;--cds-fluid-quotation-02-line-height:1.25;--cds-fluid-quotation-02-letter-spacing:0;--cds-fluid-display-01-font-size:2.625rem;--cds-fluid-display-01-font-weight:300;--cds-fluid-display-01-line-height:1.19;--cds-fluid-display-01-letter-spacing:0;--cds-fluid-display-02-font-size:2.625rem;--cds-fluid-display-02-font-weight:600;--cds-fluid-display-02-line-height:1.19;--cds-fluid-display-02-letter-spacing:0;--cds-fluid-display-03-font-size:2.625rem;--cds-fluid-display-03-font-weight:300;--cds-fluid-display-03-line-height:1.19;--cds-fluid-display-03-letter-spacing:0;--cds-fluid-display-04-font-size:2.625rem;--cds-fluid-display-04-font-weight:300;--cds-fluid-display-04-line-height:1.19;--cds-fluid-display-04-letter-spacing:0;--cds-spacing-01:.125rem;--cds-spacing-02:.25rem;--cds-spacing-03:.5rem;--cds-spacing-04:.75rem;--cds-spacing-05:1rem;--cds-spacing-06:1.5rem;--cds-spacing-07:2rem;--cds-spacing-08:2.5rem;--cds-spacing-09:3rem;--cds-spacing-10:4rem;--cds-spacing-11:5rem;--cds-spacing-12:6rem;--cds-spacing-13:10rem;--cds-fluid-spacing-01:0;--cds-fluid-spacing-02:2vw;--cds-fluid-spacing-03:5vw;--cds-fluid-spacing-04:10vw;--cds-layout-01:1rem;--cds-layout-02:1.5rem;--cds-layout-03:2rem;--cds-layout-04:3rem;--cds-layout-05:4rem;--cds-layout-06:6rem;--cds-layout-07:10rem;--cds-container-01:1.5rem;--cds-container-02:2rem;--cds-container-03:2.5rem;--cds-container-04:3rem;--cds-container-05:4rem;--cds-size-xsmall:1.5rem;--cds-size-small:2rem;--cds-size-medium:2.5rem;--cds-size-large:3rem;--cds-size-xlarge:4rem;--cds-size-2XLarge:5rem;--cds-icon-size-01:1rem;--cds-icon-size-02:1.25rem}:host{--header-height:3.5rem;--header-content-max-width:none;--cool-gray-10:#f2f4f8;--cool-gray-20:#dde1e6;--cool-gray-30:#c1c7cd;--cool-gray-60:#697077;--cool-gray-80:#343a3f;--purple-70:#6929c4;--sidenav-item-height:3.5rem;--sidenav-item-height-short:2.8rem;--box-shadow:rgb(0 0 0 / 10%) 0 2px 3px 0;display:block;font-family:'IBM Plex Sans','Helvetica Neue',Arial,sans-serif}:host(qiskit-ui-shell){margin-top:var(--header-height)}bx-header ~ bx-side-nav{height:calc(100% - var(--header-height));margin-top:var(--header-height)}bx-header{display:flex;justify-content:space-between;height:var(--header-height);border-color:var(--cds-border-subtle);background-color:var(--cds-ui-background);color:var(--cds-text-secondary)}bx-header svg.menu__account__icon{display:block;overflow:hidden;box-sizing:border-box;width:2rem;height:2rem;border:.125rem solid var(--cds-icon-on-color);border-radius:50%;fill:var(--cds-icon-on-color)}bx-header svg{width:100%;height:auto}bx-header .qiskit-header-content{display:flex;justify-content:space-between;width:100%;max-width:var(--header-content-max-width);margin-right:auto;margin-left:auto}bx-header-name::part(link){width:8.75rem;height:calc(var(--header-height) - 1px);fill:var(--cool-gray-80)}bx-header-nav{height:calc(var(--header-height) - 1px)}bx-header-nav::before,bx-header-nav::part(divider){display:none}bx-header-nav-item.qiskit-user-account-icon::part(link),bx-header-nav-item.qiskit-user-account-icon::part(link):focus,bx-header-nav-item.qiskit-user-account-icon::part(link):hover{background-color:var(--purple-70);color:var(--cds-ui-background)}bx-header-nav-item::part(link),bx-header-menu-item::part(link),qiskit-header-menu::part(trigger),qiskit-header-menu-mega::part(trigger){font-size:1rem;font-weight:400;line-height:1.5;letter-spacing:0;background:var(--cds-ui-background);color:var(--cds-text-secondary)}bx-header-nav-item::part(link):active,bx-header-nav-item::part(link):focus,bx-header-nav-item::part(link):hover,bx-header-menu-item::part(link):active,bx-header-menu-item::part(link):focus,bx-header-menu-item::part(link):hover,qiskit-header-menu::part(trigger):active,qiskit-header-menu::part(trigger):focus,qiskit-header-menu::part(trigger):hover,qiskit-header-menu-mega::part(trigger):active,qiskit-header-menu-mega::part(trigger):focus,qiskit-header-menu-mega::part(trigger):hover{background-color:var(--cds-ui-background);color:var(--cds-text-secondary)}bx-header-nav-item::part(link):hover,bx-header-menu-item::part(link):hover,qiskit-header-menu::part(trigger):hover,qiskit-header-menu-mega::part(trigger):hover{text-decoration:underline}bx-header-nav-item::part(link):focus,bx-header-menu-item::part(link):focus,qiskit-header-menu::part(trigger):focus,qiskit-header-menu-mega::part(trigger):focus{border-color:var(--cds-border-subtle)}:host(qiskit-header-menu)::part(trigger-icon){fill:var(--cds-text-secondary) !important}:host(qiskit-header-menu)::part(menu-body){box-shadow:var(--box-shadow)}:host(qiskit-header-menu) .bx--header__menu-title{background-color:var(--cds-ui-background);color:var(--cds-text-secondary)}:host(qiskit-header-menu) .bx--header__menu-title[aria-expanded=true]{background-color:var(--cool-gray-10)}bx-header-menu-item{height:3rem;background-color:var(--cds-ui-01);color:var(--cds-text-secondary)}bx-header-menu-item::part(link),bx-header-menu-item::part(link):focus,bx-header-menu-item::part(link):hover{background-color:var(--cool-gray-10)}:host(qiskit-header-menu-mega)::part(trigger-icon){fill:var(--cds-text-secondary) !important}:host(qiskit-header-menu-mega) .bx--header__menu-title{color:var(--cds-text-secondary)}:host(qiskit-header-menu-mega) .bx--header__menu-title[aria-expanded=true]{background-color:var(--cool-gray-10)}:host(qiskit-header-menu-mega) .bx--header__menu-title[aria-expanded=true]+.bx--header__menu{position:fixed;top:var(--header-height);bottom:auto;left:0;display:grid;grid-template-columns:repeat(6,1fr);width:100vw;height:auto;padding:var(--cds-spacing-05) var(--cds-spacing-03);background-color:var(--cool-gray-10);box-shadow:var(--box-shadow);transform:translateZ(0)}:host(qiskit-header-menu-item-mega){padding:0 var(--cds-spacing-04)}:host(qiskit-header-menu-item-mega) .qiskit-header-menu-item-mega-heading{font-size:1rem;font-weight:600;line-height:1.375;letter-spacing:0;margin-bottom:var(--cds-spacing-04);color:var(--cds-text-primary)}:host(qiskit-header-menu-item-mega) .qiskit-header-menu-item-mega-list{font-size:1rem;font-weight:400;line-height:1.5;letter-spacing:0}:host(qiskit-header-menu-item-mega) .qiskit-header-menu-item-mega-list li{margin-bottom:var(--cds-spacing-03)}:host(qiskit-header-menu-item-mega) .qiskit-header-menu-item-mega-list a{color:var(--cds-text-primary);text-decoration:none}:host(qiskit-header-menu-item-mega) .qiskit-header-menu-item-mega-list a:hover{text-decoration:underline}:host(qiskit-header-menu-item-mega) .qiskit-header-menu-item-mega-list a:focus{outline:2px solid var(--cds-border-subtle)}:host(qiskit-header-menu-button) .bx--header__menu-toggle,:host(qiskit-header-menu-button) .bx--header__menu-trigger{width:var(--header-height);height:var(--header-height);border:0}:host(qiskit-header-menu-button) .bx--header__menu-toggle>svg,:host(qiskit-header-menu-button) .bx--header__menu-trigger>svg{fill:var(--cds-text-secondary)}:host(qiskit-header-menu-button) .bx--header__menu-toggle:focus,:host(qiskit-header-menu-button) .bx--header__menu-toggle:active,:host(qiskit-header-menu-button) .bx--header__menu-toggle:hover,:host(qiskit-header-menu-button) .bx--header__menu-trigger:focus,:host(qiskit-header-menu-button) .bx--header__menu-trigger:active,:host(qiskit-header-menu-button) .bx--header__menu-trigger:hover{background-color:var(--cds-ui-01)}bx-side-nav{right:0;left:auto;box-shadow:0 .5rem .5rem rgba(0,0,0,0.25)}@media(max-width:41.98rem){bx-side-nav[expanded]{width:100%;max-width:none}}bx-side-nav svg.menu__account__icon{display:block;overflow:hidden;box-sizing:border-box;width:2rem;height:2rem;margin-right:var(--cds-spacing-03);border:.125rem solid var(--purple-70);border-radius:50%;fill:var(--purple-70)}bx-side-nav bx-side-nav-divider{margin-top:-1px;margin-bottom:-1px}bx-side-nav bx-side-nav-items{overflow-y:scroll;padding:0}bx-side-nav-link.qiskit-user-account-icon::part(link):hover,bx-side-nav-link.qiskit-user-account-icon::part(link){height:3rem;text-decoration-color:var(--purple-70)}bx-side-nav-link.qiskit-user-account-icon::part(title){display:flex;align-items:center;color:var(--purple-70)}bx-side-nav-link::part(link){min-height:var(--sidenav-item-height)}bx-side-nav-link::part(link):hover{text-decoration:underline;text-decoration-color:var(--cds-text-secondary)}bx-side-nav-link::part(link):focus{outline-color:var(--cds-border-subtle)}bx-side-nav-link::part(title){font-size:1rem;font-weight:400;line-height:1.5;letter-spacing:0}bx-side-nav-menu::part(expando){font-size:1rem;font-weight:400;line-height:1.5;letter-spacing:0;min-height:var(--sidenav-item-height)}bx-side-nav-menu::part(expando):hover{background-color:transparent;text-decoration:underline;text-decoration-color:var(--cds-text-secondary)}bx-side-nav-menu::part(expando):focus{outline-color:var(--cds-border-subtle)}bx-side-nav-menu[expanded]:not(.qiskit-side-nav-submenu)::part(title){font-weight:500}bx-side-nav-menu.qiskit-side-nav-submenu::part(expando){min-height:var(--sidenav-item-height-short);background-color:var(--cool-gray-10)}bx-side-nav-menu-item::part(link){min-height:var(--sidenav-item-height-short) !important}bx-side-nav-menu-item::part(link):hover{text-decoration:underline;text-decoration-color:var(--cds-text-secondary)}bx-side-nav-menu-item::part(link):focus{outline-color:var(--cds-border-subtle)}bx-side-nav-menu-item::part(title){font-size:1rem;font-weight:400;line-height:1.5;letter-spacing:0}bx-side-nav-menu-item.qiskit-nav-menu-item::part(link){background-color:var(--cool-gray-10) !important}bx-side-nav-menu-item.qiskit-nav-submenu-item{position:relative}bx-side-nav-menu-item.qiskit-nav-submenu-item::part(link){background-color:var(--cool-gray-20) !important}bx-side-nav-menu-item.qiskit-nav-submenu-item::part(link):focus{outline-color:var(--cool-gray-30)}bx-side-nav-menu-item.qiskit-nav-submenu-item::after{content:"";position:absolute;right:var(--cds-spacing-07);left:var(--cds-spacing-07);z-index:1;height:1px;background-color:var(--cool-gray-30)}bx-side-nav-menu-item.qiskit-nav-submenu-item:first-of-type{border-top:1px solid var(--cool-gray-30)}bx-side-nav-menu-item.qiskit-nav-submenu-item:last-of-type{border-bottom:1px solid var(--cool-gray-30)}@media(min-width:42rem){.qiskit-side-nav-footer{display:none}}.qiskit-side-nav-footer__social-container{margin:var(--cds-spacing-03) var(--cds-spacing-05)}.qiskit-side-nav-footer__social-icons{display:grid;grid-template-rows:1fr 1fr;grid-template-columns:25px 25px;grid-gap:var(--cds-spacing-02) var(--cds-spacing-05)}.qiskit-side-nav-footer__social-icons__icon{color:var(--cool-gray-60)}.qiskit-side-nav-footer__social-heading{font-size:.875rem;font-weight:600;line-height:1.29;letter-spacing:.16px;margin-bottom:var(--cds-spacing-04);color:var(--cool-gray-60)}.qiskit-side-nav-footer__copyright{font-size:.75rem;font-weight:400;line-height:1.34;letter-spacing:.32px;padding:var(--cds-spacing-05);background-color:var(--cool-gray-10)}`;let Fi=class extends zi{};Fi.styles=[Di],Fi=t([ne("qiskit-header-menu")],Fi);let Xi=class extends ji{};Xi.styles=[Di],Xi=t([ne("qiskit-header-menu-button")],Xi);let Wi=class extends zi{};Wi.styles=[Di],Wi=t([ne("qiskit-header-menu-mega")],Wi);let Gi=class extends te{constructor(){super(...arguments),this.item={},this.parentLabel="",this._handleClick=e=>{this.dispatchEvent(new CustomEvent("on-click",{detail:{label:`${this.parentLabel}-${e.label}`,url:e.url},bubbles:!0,composed:!0}))}}render(){var e,t,i;return R` +
+

${null===(e=this.item)||void 0===e?void 0:e.label}

+
    + ${null===(i=null===(t=this.item)||void 0===t?void 0:t.children)||void 0===i?void 0:i.map((e=>R`
  • + + ${e.label} + +
  • `))} +
+
+ `}};Gi.styles=[Di],t([ae({type:Object}),i("design:type",Object)],Gi.prototype,"item",void 0),t([ae({type:String}),i("design:type",Object)],Gi.prototype,"parentLabel",void 0),Gi=t([ne("qiskit-header-menu-item-mega")],Gi);const Yi=R` + +`,Qi=R` + +`,Ji=R` + +`,Zi=R` + +`;var Ki;!function(e){e.DEFAULT="",e.HIDE_ACCOUNT="hide-account"}(Ki||(Ki={}));const en=[{icon:Ji,label:"Twitter",url:"https://twitter.com/Qiskit"},{icon:Qi,label:"Slack",url:"https://qisk.it/join-slack"},{icon:Zi,label:"YouTube",url:"https://youtube.com/Qiskit"},{icon:Yi,label:"Medium",url:"https://medium.com/Qiskit"}],tn=[{label:"Documentation",children:[{label:"Home",url:"https://qiskit.org/documentation/"},{label:"Installation",url:"https://qiskit.org/documentation/getting_started.html"},{label:"Tutorials",url:"https://qiskit.org/documentation/tutorials.html"},{label:"API Reference",url:"https://qiskit.org/documentation/apidoc/index.html"},{label:"Contribute",url:"https://qiskit.org/documentation/contributing_to_qiskit.html"}]},{label:"Providers",url:"https://qiskit.org/providers/"},{label:"Community",children:[{label:"Events",url:"https://qiskit.org/events/"},{label:"Advocates",url:"https://qiskit.org/advocates/"},{label:"Ecosystem",url:"https://qiskit.org/ecosystem/"}]},{label:"Learn",url:"https://qiskit.org/learn/"}],nn=R` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`,sn=R` + +`,an="https://qiskit.org"===window.origin?"https://learn.qiskit.org":window.origin;e.QiskitUIShell=class extends te{constructor(){super(...arguments),this.variant=Ki.DEFAULT,this._NAV_ITEMS=tn,this._SOCIAL_LINKS=en,this._handleClick=(e,t)=>{const i=t?`${t}-${e.label}`:e.label;this.dispatchEvent(new CustomEvent("on-click",{detail:{label:i,url:e.url},bubbles:!0,composed:!0}))}}render(){return R` + +
+ + ${nn} + + + ${this._getHeaderItems()} + ${this.variant===Ki.HIDE_ACCOUNT?null:this._getAccountHeaderNavItem()} + + + +
+
+ + + + ${this._getSideNavItems()} + ${this.variant===Ki.HIDE_ACCOUNT?null:this._getAccountSideNavLink()} + + + + `}_getHeaderItems(){return this._NAV_ITEMS.map((e=>e.children?this._getHeaderMenu(e):this._getHeaderNavItem(e)))}_getHeaderNavItem(e){return R` + + ${null==e?void 0:e.label} + + `}_getHeaderMenu(e){var t;return R` + + ${null===(t=null==e?void 0:e.children)||void 0===t?void 0:t.map((t=>this._getHeaderMenuItem(t,null==e?void 0:e.label)))} + + `}_getHeaderMenuItem(e,t){return R` + + ${null==e?void 0:e.label} + + `}_getAccountHeaderNavItem(){return R` + + `}_getSideNavItems(){return this._NAV_ITEMS.map((e=>(null==e?void 0:e.children)?this._getSideNavMenu(e):this._getSideNavLink(e)))}_getSideNavLink(e){return R` + + ${null==e?void 0:e.label} + + + `}_getSideNavMenu(e){var t;return R` + + ${null===(t=null==e?void 0:e.children)||void 0===t?void 0:t.map((t=>this._getSideNavMenuItem(t,null==e?void 0:e.label)))} + + + `}_getSideNavMenuItem(e,t,i=!1){const n=i?"qiskit-nav-submenu-item":"qiskit-nav-menu-item";return R` + + ${null==e?void 0:e.label} + + `}_getAccountSideNavLink(){return R` + + `}_getSocialLinks(){return this._SOCIAL_LINKS.map((e=>R` + + `))}},e.QiskitUIShell.styles=[Di],t([ae({type:String}),i("design:type",String)],e.QiskitUIShell.prototype,"variant",void 0),e.QiskitUIShell=t([ne("qiskit-ui-shell")],e.QiskitUIShell)}({}); diff --git a/stable/0.4/_static/jupyter-sphinx.css b/stable/0.4/_static/jupyter-sphinx.css new file mode 100644 index 000000000..87724dfcc --- /dev/null +++ b/stable/0.4/_static/jupyter-sphinx.css @@ -0,0 +1,123 @@ +/* Stylesheet for jupyter-sphinx + +These styles mimic the Jupyter HTML styles. + +The default CSS (Cascading Style Sheet) class structure of jupyter-sphinx +is the following: + +jupyter_container + code_cell (optional) + stderr (optional) + output (optional) + +If the code_cell is not displayed, then there is not a jupyter_container, and +the output is provided without CSS. + +This stylesheet attempts to override the defaults of all packaged Sphinx themes +to display jupter-sphinx cells in a Jupyter-like style. + +If you want to adjust the styles, add additional custom CSS to override these +styles. + +After a build, this stylesheet is loaded from ./_static/jupyter-sphinx.css . + +*/ + + +div.jupyter_container { + padding: .4em; + margin: 0 0 .4em 0; + background-color: #FFFF; + border: 1px solid #CCC; + -moz-box-shadow: 2px 2px 4px rgba(87, 87, 87, 0.2); + -webkit-box-shadow: 2px 2px 4px rgba(87, 87, 87, 0.2); + box-shadow: 2px 2px 4px rgba(87, 87, 87, 0.2); +} +.jupyter_container div.code_cell { + border: 1px solid #cfcfcf; + border-radius: 2px; + background-color: #f7f7f7; + margin: 0 0; + overflow: auto; +} + +.jupyter_container div.code_cell pre { + padding: 4px; + margin: 0 0; + background-color: #f7f7f7; + border: none; + background: none; + box-shadow: none; + -webkit-box-shadow: none; /* for nature */ + -moz-box-shadow: none; /* for nature */ +} + +.jupyter_container div.code_cell * { + margin: 0 0; +} +div.jupyter_container div.highlight { + background-color: #f7f7f7; /* for haiku */ +} +div.jupyter_container { + padding: 0; + margin: 0; +} + +/* Prevent alabaster breaking highlight alignment */ +div.jupyter_container .hll { + padding: 0; + margin: 0; +} + +/* overrides for sphinx_rtd_theme */ +.rst-content .jupyter_container div[class^='highlight'], +.document .jupyter_container div[class^='highlight'], +.rst-content .jupyter_container pre.literal-block { + border:none; + margin: 0; + padding: 0; + background: none; + padding: 3px; + background-color: transparent; +} +/* restore Mathjax CSS, as it assumes a vertical margin. */ +.jupyter_container .MathJax_Display { + margin: 1em 0em; + text-align: center; +} +.jupyter_container .stderr { + background-color: #FCC; + border: none; + padding: 3px; +} +.jupyter_container .output { + border: none; +} +.jupyter_container div.output pre { + background-color: white; + background: none; + padding: 4px; + border: none; + box-shadow: none; + -webkit-box-shadow: none; /* for nature */ + -moz-box-shadow: none; /* for nature */ +} +.jupyter_container .code_cell td.linenos { + text-align: right; + padding: 4px 4px 4px 8px; + border-right: 1px solid #cfcfcf; + color: #999; +} +.jupyter_container .output .highlight { + background-color: #ffffff; +} +/* combine sequential jupyter cells, + by moving sequential ones up higher on y-axis */ +div.jupyter_container + div.jupyter_container { + margin: -.5em 0 .4em 0; +} + +/* Fix for sphinx_rtd_theme spacing after jupyter_container #91 */ +.rst-content .jupyter_container { + margin: 0 0 24px 0; +} diff --git a/stable/0.4/_static/language_data.js b/stable/0.4/_static/language_data.js new file mode 100644 index 000000000..250f5665f --- /dev/null +++ b/stable/0.4/_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-2023 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, is 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/stable/0.4/_static/minus.png b/stable/0.4/_static/minus.png new file mode 100644 index 000000000..d96755fda Binary files /dev/null and b/stable/0.4/_static/minus.png differ diff --git a/stable/0.4/_static/nbsphinx-broken-thumbnail.svg b/stable/0.4/_static/nbsphinx-broken-thumbnail.svg new file mode 100644 index 000000000..4919ca882 --- /dev/null +++ b/stable/0.4/_static/nbsphinx-broken-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/stable/0.4/_static/nbsphinx-code-cells.css b/stable/0.4/_static/nbsphinx-code-cells.css new file mode 100644 index 000000000..a3fb27c30 --- /dev/null +++ b/stable/0.4/_static/nbsphinx-code-cells.css @@ -0,0 +1,259 @@ +/* remove conflicting styling from Sphinx themes */ +div.nbinput.container div.prompt *, +div.nboutput.container div.prompt *, +div.nbinput.container div.input_area pre, +div.nboutput.container div.output_area pre, +div.nbinput.container div.input_area .highlight, +div.nboutput.container div.output_area .highlight { + border: none; + padding: 0; + margin: 0; + box-shadow: none; +} + +div.nbinput.container > div[class*=highlight], +div.nboutput.container > div[class*=highlight] { + margin: 0; +} + +div.nbinput.container div.prompt *, +div.nboutput.container div.prompt * { + background: none; +} + +div.nboutput.container div.output_area .highlight, +div.nboutput.container div.output_area pre { + background: unset; +} + +div.nboutput.container div.output_area div.highlight { + color: unset; /* override Pygments text color */ +} + +/* avoid gaps between output lines */ +div.nboutput.container div[class*=highlight] pre { + line-height: normal; +} + +/* input/output containers */ +div.nbinput.container, +div.nboutput.container { + display: -webkit-flex; + display: flex; + align-items: flex-start; + margin: 0; + width: 100%; +} +@media (max-width: 540px) { + div.nbinput.container, + div.nboutput.container { + flex-direction: column; + } +} + +/* input container */ +div.nbinput.container { + padding-top: 5px; +} + +/* last container */ +div.nblast.container { + padding-bottom: 5px; +} + +/* input prompt */ +div.nbinput.container div.prompt pre, +/* for sphinx_immaterial theme: */ +div.nbinput.container div.prompt pre > code { + color: #307FC1; +} + +/* output prompt */ +div.nboutput.container div.prompt pre, +/* for sphinx_immaterial theme: */ +div.nboutput.container div.prompt pre > code { + color: #BF5B3D; +} + +/* all prompts */ +div.nbinput.container div.prompt, +div.nboutput.container div.prompt { + width: 4.5ex; + padding-top: 5px; + position: relative; + user-select: none; +} + +div.nbinput.container div.prompt > div, +div.nboutput.container div.prompt > div { + position: absolute; + right: 0; + margin-right: 0.3ex; +} + +@media (max-width: 540px) { + div.nbinput.container div.prompt, + div.nboutput.container div.prompt { + width: unset; + text-align: left; + padding: 0.4em; + } + div.nboutput.container div.prompt.empty { + padding: 0; + } + + div.nbinput.container div.prompt > div, + div.nboutput.container div.prompt > div { + position: unset; + } +} + +/* disable scrollbars and line breaks on prompts */ +div.nbinput.container div.prompt pre, +div.nboutput.container div.prompt pre { + overflow: hidden; + white-space: pre; +} + +/* input/output area */ +div.nbinput.container div.input_area, +div.nboutput.container div.output_area { + -webkit-flex: 1; + flex: 1; + overflow: auto; +} +@media (max-width: 540px) { + div.nbinput.container div.input_area, + div.nboutput.container div.output_area { + width: 100%; + } +} + +/* input area */ +div.nbinput.container div.input_area { + border: 1px solid #e0e0e0; + border-radius: 2px; + /*background: #f5f5f5;*/ +} + +/* override MathJax center alignment in output cells */ +div.nboutput.container div[class*=MathJax] { + text-align: left !important; +} + +/* override sphinx.ext.imgmath center alignment in output cells */ +div.nboutput.container div.math p { + text-align: left; +} + +/* standard error */ +div.nboutput.container div.output_area.stderr { + background: #fdd; +} + +/* ANSI colors */ +.ansi-black-fg { color: #3E424D; } +.ansi-black-bg { background-color: #3E424D; } +.ansi-black-intense-fg { color: #282C36; } +.ansi-black-intense-bg { background-color: #282C36; } +.ansi-red-fg { color: #E75C58; } +.ansi-red-bg { background-color: #E75C58; } +.ansi-red-intense-fg { color: #B22B31; } +.ansi-red-intense-bg { background-color: #B22B31; } +.ansi-green-fg { color: #00A250; } +.ansi-green-bg { background-color: #00A250; } +.ansi-green-intense-fg { color: #007427; } +.ansi-green-intense-bg { background-color: #007427; } +.ansi-yellow-fg { color: #DDB62B; } +.ansi-yellow-bg { background-color: #DDB62B; } +.ansi-yellow-intense-fg { color: #B27D12; } +.ansi-yellow-intense-bg { background-color: #B27D12; } +.ansi-blue-fg { color: #208FFB; } +.ansi-blue-bg { background-color: #208FFB; } +.ansi-blue-intense-fg { color: #0065CA; } +.ansi-blue-intense-bg { background-color: #0065CA; } +.ansi-magenta-fg { color: #D160C4; } +.ansi-magenta-bg { background-color: #D160C4; } +.ansi-magenta-intense-fg { color: #A03196; } +.ansi-magenta-intense-bg { background-color: #A03196; } +.ansi-cyan-fg { color: #60C6C8; } +.ansi-cyan-bg { background-color: #60C6C8; } +.ansi-cyan-intense-fg { color: #258F8F; } +.ansi-cyan-intense-bg { background-color: #258F8F; } +.ansi-white-fg { color: #C5C1B4; } +.ansi-white-bg { background-color: #C5C1B4; } +.ansi-white-intense-fg { color: #A1A6B2; } +.ansi-white-intense-bg { background-color: #A1A6B2; } + +.ansi-default-inverse-fg { color: #FFFFFF; } +.ansi-default-inverse-bg { background-color: #000000; } + +.ansi-bold { font-weight: bold; } +.ansi-underline { text-decoration: underline; } + + +div.nbinput.container div.input_area div[class*=highlight] > pre, +div.nboutput.container div.output_area div[class*=highlight] > pre, +div.nboutput.container div.output_area div[class*=highlight].math, +div.nboutput.container div.output_area.rendered_html, +div.nboutput.container div.output_area > div.output_javascript, +div.nboutput.container div.output_area:not(.rendered_html) > img{ + padding: 5px; + margin: 0; +} + +/* fix copybtn overflow problem in chromium (needed for 'sphinx_copybutton') */ +div.nbinput.container div.input_area > div[class^='highlight'], +div.nboutput.container div.output_area > div[class^='highlight']{ + overflow-y: hidden; +} + +/* hide copy button on prompts for 'sphinx_copybutton' extension ... */ +.prompt .copybtn, +/* ... and 'sphinx_immaterial' theme */ +.prompt .md-clipboard.md-icon { + display: none; +} + +/* Some additional styling taken form the Jupyter notebook CSS */ +.jp-RenderedHTMLCommon table, +div.rendered_html table { + border: none; + border-collapse: collapse; + border-spacing: 0; + color: black; + font-size: 12px; + table-layout: fixed; +} +.jp-RenderedHTMLCommon thead, +div.rendered_html thead { + border-bottom: 1px solid black; + vertical-align: bottom; +} +.jp-RenderedHTMLCommon tr, +.jp-RenderedHTMLCommon th, +.jp-RenderedHTMLCommon td, +div.rendered_html tr, +div.rendered_html th, +div.rendered_html td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} +.jp-RenderedHTMLCommon th, +div.rendered_html th { + font-weight: bold; +} +.jp-RenderedHTMLCommon tbody tr:nth-child(odd), +div.rendered_html tbody tr:nth-child(odd) { + background: #f5f5f5; +} +.jp-RenderedHTMLCommon tbody tr:hover, +div.rendered_html tbody tr:hover { + background: rgba(66, 165, 245, 0.2); +} + diff --git a/stable/0.4/_static/nbsphinx-gallery.css b/stable/0.4/_static/nbsphinx-gallery.css new file mode 100644 index 000000000..365c27a96 --- /dev/null +++ b/stable/0.4/_static/nbsphinx-gallery.css @@ -0,0 +1,31 @@ +.nbsphinx-gallery { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 5px; + margin-top: 1em; + margin-bottom: 1em; +} + +.nbsphinx-gallery > a { + padding: 5px; + border: 1px dotted currentColor; + border-radius: 2px; + text-align: center; +} + +.nbsphinx-gallery > a:hover { + border-style: solid; +} + +.nbsphinx-gallery img { + max-width: 100%; + max-height: 100%; +} + +.nbsphinx-gallery > a > div:first-child { + display: flex; + align-items: start; + justify-content: center; + height: 120px; + margin-bottom: 5px; +} diff --git a/stable/0.4/_static/nbsphinx-no-thumbnail.svg b/stable/0.4/_static/nbsphinx-no-thumbnail.svg new file mode 100644 index 000000000..9dca7588f --- /dev/null +++ b/stable/0.4/_static/nbsphinx-no-thumbnail.svg @@ -0,0 +1,9 @@ + + + + diff --git a/stable/0.4/_static/plus.png b/stable/0.4/_static/plus.png new file mode 100644 index 000000000..7107cec93 Binary files /dev/null and b/stable/0.4/_static/plus.png differ diff --git a/stable/0.4/_static/pygments.css b/stable/0.4/_static/pygments.css new file mode 100644 index 000000000..5e9f046b4 --- /dev/null +++ b/stable/0.4/_static/pygments.css @@ -0,0 +1,249 @@ +.highlight pre { line-height: 125%; } +.highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #ffffff; } +.highlight .c { color: #888888 } /* Comment */ +.highlight .err { color: #FF0000; background-color: #FFAAAA } /* Error */ +.highlight .k { color: #008800; font-weight: bold } /* Keyword */ +.highlight .o { color: #333333 } /* Operator */ +.highlight .ch { color: #888888 } /* Comment.Hashbang */ +.highlight .cm { color: #888888 } /* Comment.Multiline */ +.highlight .cp { color: #557799 } /* Comment.Preproc */ +.highlight .cpf { color: #888888 } /* Comment.PreprocFile */ +.highlight .c1 { color: #888888 } /* Comment.Single */ +.highlight .cs { color: #cc0000; font-weight: bold } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #333399; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #6600EE; font-weight: bold } /* Literal.Number */ +.highlight .s { background-color: #fff0f0 } /* Literal.String */ +.highlight .na { color: #0000CC } /* Name.Attribute */ +.highlight .nb { color: #007020 } /* Name.Builtin */ +.highlight .nc { color: #BB0066; font-weight: bold } /* Name.Class */ +.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ +.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #880000; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #FF0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0066BB; font-weight: bold } /* Name.Function */ +.highlight .nl { color: #997700; font-weight: bold } /* Name.Label */ +.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #007700 } /* Name.Tag */ +.highlight .nv { color: #996633 } /* Name.Variable */ +.highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */ +.highlight .mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */ +.highlight .mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */ +.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ +.highlight .mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */ +.highlight .sa { background-color: #fff0f0 } /* Literal.String.Affix */ +.highlight .sb { background-color: #fff0f0 } /* Literal.String.Backtick */ +.highlight .sc { color: #0044DD } /* Literal.String.Char */ +.highlight .dl { background-color: #fff0f0 } /* Literal.String.Delimiter */ +.highlight .sd { color: #DD4422 } /* Literal.String.Doc */ +.highlight .s2 { background-color: #fff0f0 } /* Literal.String.Double */ +.highlight .se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */ +.highlight .sh { background-color: #fff0f0 } /* Literal.String.Heredoc */ +.highlight .si { background-color: #eeeeee } /* Literal.String.Interpol */ +.highlight .sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */ +.highlight .sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */ +.highlight .s1 { background-color: #fff0f0 } /* Literal.String.Single */ +.highlight .ss { color: #AA6600 } /* Literal.String.Symbol */ +.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0066BB; font-weight: bold } /* Name.Function.Magic */ +.highlight .vc { color: #336699 } /* Name.Variable.Class */ +.highlight .vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */ +.highlight .vi { color: #3333BB } /* Name.Variable.Instance */ +.highlight .vm { color: #996633 } /* Name.Variable.Magic */ +.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ +@media not print { +body[data-theme="dark"] .highlight pre { line-height: 125%; } +body[data-theme="dark"] .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight .hll { background-color: #404040 } +body[data-theme="dark"] .highlight { background: #202020; color: #d0d0d0 } +body[data-theme="dark"] .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body[data-theme="dark"] .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body[data-theme="dark"] .highlight .esc { color: #d0d0d0 } /* Escape */ +body[data-theme="dark"] .highlight .g { color: #d0d0d0 } /* Generic */ +body[data-theme="dark"] .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body[data-theme="dark"] .highlight .l { color: #d0d0d0 } /* Literal */ +body[data-theme="dark"] .highlight .n { color: #d0d0d0 } /* Name */ +body[data-theme="dark"] .highlight .o { color: #d0d0d0 } /* Operator */ +body[data-theme="dark"] .highlight .x { color: #d0d0d0 } /* Other */ +body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */ +body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body[data-theme="dark"] .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body[data-theme="dark"] .highlight .gd { color: #d22323 } /* Generic.Deleted */ +body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body[data-theme="dark"] .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body[data-theme="dark"] .highlight .gr { color: #d22323 } /* Generic.Error */ +body[data-theme="dark"] .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */ +body[data-theme="dark"] .highlight .go { color: #cccccc } /* Generic.Output */ +body[data-theme="dark"] .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body[data-theme="dark"] .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body[data-theme="dark"] .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body[data-theme="dark"] .highlight .gt { color: #d22323 } /* Generic.Traceback */ +body[data-theme="dark"] .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body[data-theme="dark"] .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body[data-theme="dark"] .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body[data-theme="dark"] .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body[data-theme="dark"] .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body[data-theme="dark"] .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body[data-theme="dark"] .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body[data-theme="dark"] .highlight .m { color: #51b2fd } /* Literal.Number */ +body[data-theme="dark"] .highlight .s { color: #ed9d13 } /* Literal.String */ +body[data-theme="dark"] .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body[data-theme="dark"] .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body[data-theme="dark"] .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body[data-theme="dark"] .highlight .no { color: #40ffff } /* Name.Constant */ +body[data-theme="dark"] .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body[data-theme="dark"] .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body[data-theme="dark"] .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body[data-theme="dark"] .highlight .nf { color: #71adff } /* Name.Function */ +body[data-theme="dark"] .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body[data-theme="dark"] .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body[data-theme="dark"] .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body[data-theme="dark"] .highlight .py { color: #d0d0d0 } /* Name.Property */ +body[data-theme="dark"] .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body[data-theme="dark"] .highlight .nv { color: #40ffff } /* Name.Variable */ +body[data-theme="dark"] .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body[data-theme="dark"] .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body[data-theme="dark"] .highlight .w { color: #666666 } /* Text.Whitespace */ +body[data-theme="dark"] .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body[data-theme="dark"] .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body[data-theme="dark"] .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body[data-theme="dark"] .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body[data-theme="dark"] .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body[data-theme="dark"] .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body[data-theme="dark"] .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body[data-theme="dark"] .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body[data-theme="dark"] .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body[data-theme="dark"] .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body[data-theme="dark"] .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body[data-theme="dark"] .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body[data-theme="dark"] .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body[data-theme="dark"] .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body[data-theme="dark"] .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body[data-theme="dark"] .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body[data-theme="dark"] .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body[data-theme="dark"] .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body[data-theme="dark"] .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body[data-theme="dark"] .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body[data-theme="dark"] .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body[data-theme="dark"] .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body[data-theme="dark"] .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body[data-theme="dark"] .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body[data-theme="dark"] .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +@media (prefers-color-scheme: dark) { +body:not([data-theme="light"]) .highlight pre { line-height: 125%; } +body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight .hll { background-color: #404040 } +body:not([data-theme="light"]) .highlight { background: #202020; color: #d0d0d0 } +body:not([data-theme="light"]) .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body:not([data-theme="light"]) .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body:not([data-theme="light"]) .highlight .esc { color: #d0d0d0 } /* Escape */ +body:not([data-theme="light"]) .highlight .g { color: #d0d0d0 } /* Generic */ +body:not([data-theme="light"]) .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body:not([data-theme="light"]) .highlight .l { color: #d0d0d0 } /* Literal */ +body:not([data-theme="light"]) .highlight .n { color: #d0d0d0 } /* Name */ +body:not([data-theme="light"]) .highlight .o { color: #d0d0d0 } /* Operator */ +body:not([data-theme="light"]) .highlight .x { color: #d0d0d0 } /* Other */ +body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */ +body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body:not([data-theme="light"]) .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body:not([data-theme="light"]) .highlight .gd { color: #d22323 } /* Generic.Deleted */ +body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body:not([data-theme="light"]) .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body:not([data-theme="light"]) .highlight .gr { color: #d22323 } /* Generic.Error */ +body:not([data-theme="light"]) .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */ +body:not([data-theme="light"]) .highlight .go { color: #cccccc } /* Generic.Output */ +body:not([data-theme="light"]) .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body:not([data-theme="light"]) .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body:not([data-theme="light"]) .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body:not([data-theme="light"]) .highlight .gt { color: #d22323 } /* Generic.Traceback */ +body:not([data-theme="light"]) .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body:not([data-theme="light"]) .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body:not([data-theme="light"]) .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body:not([data-theme="light"]) .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body:not([data-theme="light"]) .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body:not([data-theme="light"]) .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body:not([data-theme="light"]) .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body:not([data-theme="light"]) .highlight .m { color: #51b2fd } /* Literal.Number */ +body:not([data-theme="light"]) .highlight .s { color: #ed9d13 } /* Literal.String */ +body:not([data-theme="light"]) .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body:not([data-theme="light"]) .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body:not([data-theme="light"]) .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body:not([data-theme="light"]) .highlight .no { color: #40ffff } /* Name.Constant */ +body:not([data-theme="light"]) .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body:not([data-theme="light"]) .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body:not([data-theme="light"]) .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body:not([data-theme="light"]) .highlight .nf { color: #71adff } /* Name.Function */ +body:not([data-theme="light"]) .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body:not([data-theme="light"]) .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body:not([data-theme="light"]) .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body:not([data-theme="light"]) .highlight .py { color: #d0d0d0 } /* Name.Property */ +body:not([data-theme="light"]) .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body:not([data-theme="light"]) .highlight .nv { color: #40ffff } /* Name.Variable */ +body:not([data-theme="light"]) .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body:not([data-theme="light"]) .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body:not([data-theme="light"]) .highlight .w { color: #666666 } /* Text.Whitespace */ +body:not([data-theme="light"]) .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body:not([data-theme="light"]) .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body:not([data-theme="light"]) .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body:not([data-theme="light"]) .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body:not([data-theme="light"]) .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body:not([data-theme="light"]) .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body:not([data-theme="light"]) .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body:not([data-theme="light"]) .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body:not([data-theme="light"]) .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body:not([data-theme="light"]) .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body:not([data-theme="light"]) .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body:not([data-theme="light"]) .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body:not([data-theme="light"]) .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body:not([data-theme="light"]) .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body:not([data-theme="light"]) .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body:not([data-theme="light"]) .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body:not([data-theme="light"]) .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body:not([data-theme="light"]) .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body:not([data-theme="light"]) .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body:not([data-theme="light"]) .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body:not([data-theme="light"]) .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body:not([data-theme="light"]) .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body:not([data-theme="light"]) .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body:not([data-theme="light"]) .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body:not([data-theme="light"]) .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +} +} \ No newline at end of file diff --git a/stable/0.4/_static/scripts/furo-extensions.js b/stable/0.4/_static/scripts/furo-extensions.js new file mode 100644 index 000000000..e69de29bb diff --git a/stable/0.4/_static/scripts/furo.js b/stable/0.4/_static/scripts/furo.js new file mode 100644 index 000000000..32e7c05be --- /dev/null +++ b/stable/0.4/_static/scripts/furo.js @@ -0,0 +1,3 @@ +/*! For license information please see furo.js.LICENSE.txt */ +(()=>{var t={212:function(t,e,n){var o,r;r=void 0!==n.g?n.g:"undefined"!=typeof window?window:this,o=function(){return function(t){"use strict";var e={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},n=function(t,e,n){if(n.settings.events){var o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n});e.dispatchEvent(o)}},o=function(t){var e=0;if(t.offsetParent)for(;t;)e+=t.offsetTop,t=t.offsetParent;return e>=0?e:0},r=function(t){t&&t.sort((function(t,e){return o(t.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},l=function(t,e){var n=t[t.length-1];if(function(t,e){return!(!s()||!c(t.content,e,!0))}(n,e))return n;for(var o=t.length-1;o>=0;o--)if(c(t[o].content,e))return t[o]},a=function(t,e){if(e.nested&&t.parentNode){var n=t.parentNode.closest("li");n&&(n.classList.remove(e.nestedClass),a(n,e))}},i=function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.remove(e.navClass),t.content.classList.remove(e.contentClass),a(o,e),n("gumshoeDeactivate",o,{link:t.nav,content:t.content,settings:e}))}},u=function(t,e){if(e.nested){var n=t.parentNode.closest("li");n&&(n.classList.add(e.nestedClass),u(n,e))}};return function(o,c){var s,a,d,f,m,v={setup:function(){s=document.querySelectorAll(o),a=[],Array.prototype.forEach.call(s,(function(t){var e=document.getElementById(decodeURIComponent(t.hash.substr(1)));e&&a.push({nav:t,content:e})})),r(a)},detect:function(){var t=l(a,m);t?d&&t.content===d.content||(i(d,m),function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.add(e.navClass),t.content.classList.add(e.contentClass),u(o,e),n("gumshoeActivate",o,{link:t.nav,content:t.content,settings:e}))}}(t,m),d=t):d&&(i(d,m),d=null)}},h=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame(v.detect)},g=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame((function(){r(a),v.detect()}))};return v.destroy=function(){d&&i(d,m),t.removeEventListener("scroll",h,!1),m.reflow&&t.removeEventListener("resize",g,!1),a=null,s=null,d=null,f=null,m=null},m=function(){var t={};return Array.prototype.forEach.call(arguments,(function(e){for(var n in e){if(!e.hasOwnProperty(n))return;t[n]=e[n]}})),t}(e,c||{}),v.setup(),v.detect(),t.addEventListener("scroll",h,!1),m.reflow&&t.addEventListener("resize",g,!1),v}}(r)}.apply(e,[]),void 0===o||(t.exports=o)}},e={};function n(o){var r=e[o];if(void 0!==r)return r.exports;var c=e[o]={exports:{}};return t[o].call(c.exports,c,c.exports,n),c.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";var t=n(212),e=n.n(t),o=null,r=null,c=window.pageYOffset||document.documentElement.scrollTop;const s=64;function l(){const t=localStorage.getItem("theme")||"auto";var e;"light"!==(e=window.matchMedia("(prefers-color-scheme: dark)").matches?"auto"===t?"light":"light"==t?"dark":"auto":"auto"===t?"dark":"dark"==t?"light":"auto")&&"dark"!==e&&"auto"!==e&&(console.error(`Got invalid theme mode: ${e}. Resetting to auto.`),e="auto"),document.body.dataset.theme=e,localStorage.setItem("theme",e),console.log(`Changed to ${e} mode.`)}function a(){!function(){const t=document.getElementsByClassName("theme-toggle");Array.from(t).forEach((t=>{t.addEventListener("click",l)}))}(),function(){let t=0,e=!1;window.addEventListener("scroll",(function(n){t=window.scrollY,e||(window.requestAnimationFrame((function(){var n;n=t,0==Math.floor(r.getBoundingClientRect().top)?r.classList.add("scrolled"):r.classList.remove("scrolled"),function(t){tc&&document.documentElement.classList.remove("show-back-to-top"),c=t}(n),function(t){null!==o&&(0==t?o.scrollTo(0,0):Math.ceil(t)>=Math.floor(document.documentElement.scrollHeight-window.innerHeight)?o.scrollTo(0,o.scrollHeight):document.querySelector(".scroll-current"))}(n),e=!1})),e=!0)})),window.scroll()}(),null!==o&&new(e())(".toc-tree a",{reflow:!0,recursive:!0,navClass:"scroll-current",offset:()=>{let t=parseFloat(getComputedStyle(document.documentElement).fontSize);return r.getBoundingClientRect().height+.5*t+1}})}document.addEventListener("DOMContentLoaded",(function(){document.body.parentNode.classList.remove("no-js"),r=document.querySelector("header"),o=document.querySelector(".toc-scroll"),a()}))})()})(); +//# sourceMappingURL=furo.js.map \ No newline at end of file diff --git a/stable/0.4/_static/scripts/furo.js.LICENSE.txt b/stable/0.4/_static/scripts/furo.js.LICENSE.txt new file mode 100644 index 000000000..1632189c7 --- /dev/null +++ b/stable/0.4/_static/scripts/furo.js.LICENSE.txt @@ -0,0 +1,7 @@ +/*! + * gumshoejs v5.1.2 (patched by @pradyunsg) + * A simple, framework-agnostic scrollspy script. + * (c) 2019 Chris Ferdinandi + * MIT License + * http://github.com/cferdinandi/gumshoe + */ diff --git a/stable/0.4/_static/scripts/furo.js.map b/stable/0.4/_static/scripts/furo.js.map new file mode 100644 index 000000000..470530223 --- /dev/null +++ b/stable/0.4/_static/scripts/furo.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/furo.js","mappings":";iCAAA,MAQWA,SAWS,IAAX,EAAAC,EACH,EAAAA,EACkB,oBAAXC,OACLA,OACAC,KAbO,EAAF,WACP,OAaJ,SAAUD,GACR,aAMA,IAAIE,EAAW,CAEbC,SAAU,SACVC,aAAc,SAGdC,QAAQ,EACRC,YAAa,SAGbC,OAAQ,EACRC,QAAQ,EAGRC,QAAQ,GA6BNC,EAAY,SAAUC,EAAMC,EAAMC,GAEpC,GAAKA,EAAOC,SAASL,OAArB,CAGA,IAAIM,EAAQ,IAAIC,YAAYL,EAAM,CAChCM,SAAS,EACTC,YAAY,EACZL,OAAQA,IAIVD,EAAKO,cAAcJ,EAVgB,CAWrC,EAOIK,EAAe,SAAUR,GAC3B,IAAIS,EAAW,EACf,GAAIT,EAAKU,aACP,KAAOV,GACLS,GAAYT,EAAKW,UACjBX,EAAOA,EAAKU,aAGhB,OAAOD,GAAY,EAAIA,EAAW,CACpC,EAMIG,EAAe,SAAUC,GACvBA,GACFA,EAASC,MAAK,SAAUC,EAAOC,GAG7B,OAFcR,EAAaO,EAAME,SACnBT,EAAaQ,EAAMC,UACF,EACxB,CACT,GAEJ,EAwCIC,EAAW,SAAUlB,EAAME,EAAUiB,GACvC,IAAIC,EAASpB,EAAKqB,wBACd1B,EAnCU,SAAUO,GAExB,MAA+B,mBAApBA,EAASP,OACX2B,WAAWpB,EAASP,UAItB2B,WAAWpB,EAASP,OAC7B,CA2Be4B,CAAUrB,GACvB,OAAIiB,EAEAK,SAASJ,EAAOD,OAAQ,KACvB/B,EAAOqC,aAAeC,SAASC,gBAAgBC,cAG7CJ,SAASJ,EAAOS,IAAK,KAAOlC,CACrC,EAMImC,EAAa,WACf,OACEC,KAAKC,KAAK5C,EAAOqC,YAAcrC,EAAO6C,cAnCjCF,KAAKG,IACVR,SAASS,KAAKC,aACdV,SAASC,gBAAgBS,aACzBV,SAASS,KAAKE,aACdX,SAASC,gBAAgBU,aACzBX,SAASS,KAAKP,aACdF,SAASC,gBAAgBC,aAkC7B,EAmBIU,EAAY,SAAUzB,EAAUX,GAClC,IAAIqC,EAAO1B,EAASA,EAAS2B,OAAS,GACtC,GAbgB,SAAUC,EAAMvC,GAChC,SAAI4B,MAAgBZ,EAASuB,EAAKxB,QAASf,GAAU,GAEvD,CAUMwC,CAAYH,EAAMrC,GAAW,OAAOqC,EACxC,IAAK,IAAII,EAAI9B,EAAS2B,OAAS,EAAGG,GAAK,EAAGA,IACxC,GAAIzB,EAASL,EAAS8B,GAAG1B,QAASf,GAAW,OAAOW,EAAS8B,EAEjE,EAOIC,EAAmB,SAAUC,EAAK3C,GAEpC,GAAKA,EAAST,QAAWoD,EAAIC,WAA7B,CAGA,IAAIC,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASR,aAG7BkD,EAAiBG,EAAI7C,GAV0B,CAWjD,EAOIiD,EAAa,SAAUC,EAAOlD,GAEhC,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASX,UAC7B6D,EAAMnC,QAAQgC,UAAUC,OAAOhD,EAASV,cAGxCoD,EAAiBG,EAAI7C,GAGrBJ,EAAU,oBAAqBiD,EAAI,CACjCM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,EAOIoD,EAAiB,SAAUT,EAAK3C,GAElC,GAAKA,EAAST,OAAd,CAGA,IAAIsD,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASR,aAG1B4D,EAAeP,EAAI7C,GAVS,CAW9B,EA6LA,OA1JkB,SAAUsD,EAAUC,GAKpC,IACIC,EAAU7C,EAAU8C,EAASC,EAAS1D,EADtC2D,EAAa,CAUjBA,MAAmB,WAEjBH,EAAWhC,SAASoC,iBAAiBN,GAGrC3C,EAAW,GAGXkD,MAAMC,UAAUC,QAAQC,KAAKR,GAAU,SAAUjB,GAE/C,IAAIxB,EAAUS,SAASyC,eACrBC,mBAAmB3B,EAAK4B,KAAKC,OAAO,KAEjCrD,GAGLJ,EAAS0D,KAAK,CACZ1B,IAAKJ,EACLxB,QAASA,GAEb,IAGAL,EAAaC,EACf,EAKAgD,OAAoB,WAElB,IAAIW,EAASlC,EAAUzB,EAAUX,GAG5BsE,EASDb,GAAWa,EAAOvD,UAAY0C,EAAQ1C,UAG1CkC,EAAWQ,EAASzD,GAzFT,SAAUkD,EAAOlD,GAE9B,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASX,UAC1B6D,EAAMnC,QAAQgC,UAAUM,IAAIrD,EAASV,cAGrC8D,EAAeP,EAAI7C,GAGnBJ,EAAU,kBAAmBiD,EAAI,CAC/BM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,CAqEIuE,CAASD,EAAQtE,GAGjByD,EAAUa,GAfJb,IACFR,EAAWQ,EAASzD,GACpByD,EAAU,KAchB,GAMIe,EAAgB,SAAUvE,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,sBAAsBf,EAAWgB,OACpD,EAMIC,EAAgB,SAAU3E,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,uBAAsB,WACrChE,EAAaC,GACbgD,EAAWgB,QACb,GACF,EAkDA,OA7CAhB,EAAWkB,QAAU,WAEfpB,GACFR,EAAWQ,EAASzD,GAItBd,EAAO4F,oBAAoB,SAAUN,GAAe,GAChDxE,EAASN,QACXR,EAAO4F,oBAAoB,SAAUF,GAAe,GAItDjE,EAAW,KACX6C,EAAW,KACXC,EAAU,KACVC,EAAU,KACV1D,EAAW,IACb,EAOEA,EA3XS,WACX,IAAI+E,EAAS,CAAC,EAOd,OANAlB,MAAMC,UAAUC,QAAQC,KAAKgB,WAAW,SAAUC,GAChD,IAAK,IAAIC,KAAOD,EAAK,CACnB,IAAKA,EAAIE,eAAeD,GAAM,OAC9BH,EAAOG,GAAOD,EAAIC,EACpB,CACF,IACOH,CACT,CAkXeK,CAAOhG,EAAUmE,GAAW,CAAC,GAGxCI,EAAW0B,QAGX1B,EAAWgB,SAGXzF,EAAOoG,iBAAiB,SAAUd,GAAe,GAC7CxE,EAASN,QACXR,EAAOoG,iBAAiB,SAAUV,GAAe,GAS9CjB,CACT,CAOF,CArcW4B,CAAQvG,EAChB,UAFM,SAEN,uBCXDwG,EAA2B,CAAC,EAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaE,QAGrB,IAAIC,EAASN,EAAyBE,GAAY,CAGjDG,QAAS,CAAC,GAOX,OAHAE,EAAoBL,GAAU1B,KAAK8B,EAAOD,QAASC,EAAQA,EAAOD,QAASJ,GAGpEK,EAAOD,OACf,CCrBAJ,EAAoBO,EAAKF,IACxB,IAAIG,EAASH,GAAUA,EAAOI,WAC7B,IAAOJ,EAAiB,QACxB,IAAM,EAEP,OADAL,EAAoBU,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdR,EAAoBU,EAAI,CAACN,EAASQ,KACjC,IAAI,IAAInB,KAAOmB,EACXZ,EAAoBa,EAAED,EAAYnB,KAASO,EAAoBa,EAAET,EAASX,IAC5EqB,OAAOC,eAAeX,EAASX,EAAK,CAAEuB,YAAY,EAAMC,IAAKL,EAAWnB,IAE1E,ECNDO,EAAoBxG,EAAI,WACvB,GAA0B,iBAAf0H,WAAyB,OAAOA,WAC3C,IACC,OAAOxH,MAAQ,IAAIyH,SAAS,cAAb,EAChB,CAAE,MAAOC,GACR,GAAsB,iBAAX3H,OAAqB,OAAOA,MACxC,CACA,CAPuB,GCAxBuG,EAAoBa,EAAI,CAACrB,EAAK6B,IAAUP,OAAOzC,UAAUqB,eAAenB,KAAKiB,EAAK6B,4CCK9EC,EAAY,KACZC,EAAS,KACTC,EAAgB/H,OAAO6C,aAAeP,SAASC,gBAAgByF,UACnE,MAAMC,EAAmB,GA2EzB,SAASC,IACP,MAAMC,EAAeC,aAAaC,QAAQ,UAAY,OAZxD,IAAkBC,EACH,WADGA,EAaItI,OAAOuI,WAAW,gCAAgCC,QAI/C,SAAjBL,EACO,QACgB,SAAhBA,EACA,OAEA,OAIU,SAAjBA,EACO,OACgB,QAAhBA,EACA,QAEA,SA9BoB,SAATG,GAA4B,SAATA,IACzCG,QAAQC,MAAM,2BAA2BJ,yBACzCA,EAAO,QAGThG,SAASS,KAAK4F,QAAQC,MAAQN,EAC9BF,aAAaS,QAAQ,QAASP,GAC9BG,QAAQK,IAAI,cAAcR,UA0B5B,CAkDA,SAASnC,KART,WAEE,MAAM4C,EAAUzG,SAAS0G,uBAAuB,gBAChDrE,MAAMsE,KAAKF,GAASlE,SAASqE,IAC3BA,EAAI9C,iBAAiB,QAAS8B,EAAe,GAEjD,CAGEiB,GA9CF,WAEE,IAAIC,EAA6B,EAC7BC,GAAU,EAEdrJ,OAAOoG,iBAAiB,UAAU,SAAUuB,GAC1CyB,EAA6BpJ,OAAOsJ,QAE/BD,IACHrJ,OAAOwF,uBAAsB,WAzDnC,IAAuB+D,IA0DDH,EA9GkC,GAAlDzG,KAAK6G,MAAM1B,EAAO7F,wBAAwBQ,KAC5CqF,EAAOjE,UAAUM,IAAI,YAErB2D,EAAOjE,UAAUC,OAAO,YAI5B,SAAmCyF,GAC7BA,EAAYtB,EACd3F,SAASC,gBAAgBsB,UAAUC,OAAO,oBAEtCyF,EAAYxB,EACdzF,SAASC,gBAAgBsB,UAAUM,IAAI,oBAC9BoF,EAAYxB,GACrBzF,SAASC,gBAAgBsB,UAAUC,OAAO,oBAG9CiE,EAAgBwB,CAClB,CAoCEE,CAA0BF,GAlC5B,SAA6BA,GACT,OAAd1B,IAKa,GAAb0B,EACF1B,EAAU6B,SAAS,EAAG,GAGtB/G,KAAKC,KAAK2G,IACV5G,KAAK6G,MAAMlH,SAASC,gBAAgBS,aAAehD,OAAOqC,aAE1DwF,EAAU6B,SAAS,EAAG7B,EAAU7E,cAGhBV,SAASqH,cAAc,mBAc3C,CAKEC,CAAoBL,GAwDdF,GAAU,CACZ,IAEAA,GAAU,EAEd,IACArJ,OAAO6J,QACT,CA6BEC,GA1BkB,OAAdjC,GAKJ,IAAI,IAAJ,CAAY,cAAe,CACzBrH,QAAQ,EACRuJ,WAAW,EACX5J,SAAU,iBACVI,OAAQ,KACN,IAAIyJ,EAAM9H,WAAW+H,iBAAiB3H,SAASC,iBAAiB2H,UAChE,OAAOpC,EAAO7F,wBAAwBkI,OAAS,GAAMH,EAAM,CAAC,GAiBlE,CAcA1H,SAAS8D,iBAAiB,oBAT1B,WACE9D,SAASS,KAAKW,WAAWG,UAAUC,OAAO,SAE1CgE,EAASxF,SAASqH,cAAc,UAChC9B,EAAYvF,SAASqH,cAAc,eAEnCxD,GACF","sources":["webpack:///./src/furo/assets/scripts/gumshoe-patched.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///./src/furo/assets/scripts/furo.js"],"sourcesContent":["/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight,\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1)),\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n },\n);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\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__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","import Gumshoe from \"./gumshoe-patched.js\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\nvar lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;\nconst GO_TO_TOP_OFFSET = 64;\n\nfunction scrollHandlerForHeader() {\n if (Math.floor(header.getBoundingClientRect().top) == 0) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForBackToTop(positionY) {\n if (positionY < GO_TO_TOP_OFFSET) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n } else {\n if (positionY < lastScrollTop) {\n document.documentElement.classList.add(\"show-back-to-top\");\n } else if (positionY > lastScrollTop) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n }\n }\n lastScrollTop = positionY;\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader();\n scrollHandlerForBackToTop(positionY);\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n let last_known_scroll_position = 0;\n let ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n offset: () => {\n let rem = parseFloat(getComputedStyle(document.documentElement).fontSize);\n return header.getBoundingClientRect().height + 0.5 * rem + 1;\n },\n });\n}\n\nfunction setupTheme() {\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"],"names":["root","g","window","this","defaults","navClass","contentClass","nested","nestedClass","offset","reflow","events","emitEvent","type","elem","detail","settings","event","CustomEvent","bubbles","cancelable","dispatchEvent","getOffsetTop","location","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","bottom","bounds","getBoundingClientRect","parseFloat","getOffset","parseInt","innerHeight","document","documentElement","clientHeight","top","isAtBottom","Math","ceil","pageYOffset","max","body","scrollHeight","offsetHeight","getActive","last","length","item","useLastItem","i","deactivateNested","nav","parentNode","li","closest","classList","remove","deactivate","items","link","activateNested","add","selector","options","navItems","current","timeout","publicAPIs","querySelectorAll","Array","prototype","forEach","call","getElementById","decodeURIComponent","hash","substr","push","active","activate","scrollHandler","cancelAnimationFrame","requestAnimationFrame","detect","resizeHandler","destroy","removeEventListener","merged","arguments","obj","key","hasOwnProperty","extend","setup","addEventListener","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","n","getter","__esModule","d","a","definition","o","Object","defineProperty","enumerable","get","globalThis","Function","e","prop","tocScroll","header","lastScrollTop","scrollTop","GO_TO_TOP_OFFSET","cycleThemeOnce","currentTheme","localStorage","getItem","mode","matchMedia","matches","console","error","dataset","theme","setItem","log","buttons","getElementsByClassName","from","btn","setupTheme","last_known_scroll_position","ticking","scrollY","positionY","floor","scrollHandlerForBackToTop","scrollTo","querySelector","scrollHandlerForTOC","scroll","setupScrollHandler","recursive","rem","getComputedStyle","fontSize","height"],"sourceRoot":""} \ No newline at end of file diff --git a/stable/0.4/_static/scripts/qiskit-sphinx-theme.js b/stable/0.4/_static/scripts/qiskit-sphinx-theme.js new file mode 100644 index 000000000..c047a7a8d --- /dev/null +++ b/stable/0.4/_static/scripts/qiskit-sphinx-theme.js @@ -0,0 +1,3 @@ +/*! For license information please see qiskit-sphinx-theme.js.LICENSE.txt */ +(()=>{var e={729:function(e,t,n){var o,r;r=void 0!==n.g?n.g:"undefined"!=typeof window?window:this,o=function(){return function(e){"use strict";var t={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},n=function(e,t,n){if(n.settings.events){var o=new CustomEvent(e,{bubbles:!0,cancelable:!0,detail:n});t.dispatchEvent(o)}},o=function(e){var t=0;if(e.offsetParent)for(;e;)t+=e.offsetTop,e=e.offsetParent;return t>=0?t:0},r=function(e){e&&e.sort((function(e,t){return o(e.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},l=function(e,t){var n=e[e.length-1];if(function(e,t){return!(!s()||!c(e.content,t,!0))}(n,t))return n;for(var o=e.length-1;o>=0;o--)if(c(e[o].content,t))return e[o]},i=function(e,t){if(t.nested&&e.parentNode){var n=e.parentNode.closest("li");n&&(n.classList.remove(t.nestedClass),i(n,t))}},a=function(e,t){if(e){var o=e.nav.closest("li");o&&(o.classList.remove(t.navClass),e.content.classList.remove(t.contentClass),i(o,t),n("gumshoeDeactivate",o,{link:e.nav,content:e.content,settings:t}))}},u=function(e,t){if(t.nested){var n=e.parentNode.closest("li");n&&(n.classList.add(t.nestedClass),u(n,t))}};return function(o,c){var s,i,d,f,m,v={setup:function(){s=document.querySelectorAll(o),i=[],Array.prototype.forEach.call(s,(function(e){var t=document.getElementById(decodeURIComponent(e.hash.substr(1)));t&&i.push({nav:e,content:t})})),r(i)},detect:function(){var e=l(i,m);e?d&&e.content===d.content||(a(d,m),function(e,t){if(e){var o=e.nav.closest("li");o&&(o.classList.add(t.navClass),e.content.classList.add(t.contentClass),u(o,t),n("gumshoeActivate",o,{link:e.nav,content:e.content,settings:t}))}}(e,m),d=e):d&&(a(d,m),d=null)}},h=function(t){f&&e.cancelAnimationFrame(f),f=e.requestAnimationFrame(v.detect)},g=function(t){f&&e.cancelAnimationFrame(f),f=e.requestAnimationFrame((function(){r(i),v.detect()}))};return v.destroy=function(){d&&a(d,m),e.removeEventListener("scroll",h,!1),m.reflow&&e.removeEventListener("resize",g,!1),i=null,s=null,d=null,f=null,m=null},m=function(){var e={};return Array.prototype.forEach.call(arguments,(function(t){for(var n in t){if(!t.hasOwnProperty(n))return;e[n]=t[n]}})),e}(t,c||{}),v.setup(),v.detect(),e.addEventListener("scroll",h,!1),m.reflow&&e.addEventListener("resize",g,!1),v}}(r)}.apply(t,[]),void 0===o||(e.exports=o)}},t={};function n(o){var r=t[o];if(void 0!==r)return r.exports;var c=t[o]={exports:{}};return e[o].call(c.exports,c,c.exports,n),c.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var o in t)n.o(t,o)&&!n.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";var e=n(729),t=n.n(e),o=null,r=null,c=window.pageYOffset||document.documentElement.scrollTop;const s=64;function l(){const e=localStorage.getItem("theme")||"auto";var t;"light"!==(t=window.matchMedia("(prefers-color-scheme: dark)").matches?"auto"===e?"light":"light"==e?"dark":"auto":"auto"===e?"dark":"dark"==e?"light":"auto")&&"dark"!==t&&"auto"!==t&&(console.error(`Got invalid theme mode: ${t}. Resetting to auto.`),t="auto"),document.body.dataset.theme=t,localStorage.setItem("theme",t),console.log(`Changed to ${t} mode.`)}function i(){!function(){const e=document.getElementsByClassName("theme-toggle");Array.from(e).forEach((e=>{e.addEventListener("click",l)}))}(),function(){let e=0,t=!1;window.addEventListener("scroll",(function(n){e=window.scrollY,t||(window.requestAnimationFrame((function(){var n;n=e,0==Math.floor(r.getBoundingClientRect().top)?r.classList.add("scrolled"):r.classList.remove("scrolled"),function(e){ec&&document.documentElement.classList.remove("show-back-to-top"),c=e}(n),function(e){null!==o&&(0==e?o.scrollTo(0,0):Math.ceil(e)>=Math.floor(document.documentElement.scrollHeight-window.innerHeight)?o.scrollTo(0,o.scrollHeight):document.querySelector(".scroll-current"))}(n),t=!1})),t=!0)})),window.scroll()}(),null!==o&&new(t())(".toc-tree a",{reflow:!0,recursive:!0,navClass:"scroll-current",offset:()=>{let e=parseFloat(getComputedStyle(document.documentElement).fontSize);const t=document.querySelector("qiskit-ui-shell"),n=t&&"none"!==getComputedStyle(t).display?3.5*e:0;return r.getBoundingClientRect().height+.5*e+1+n}})}document.addEventListener("DOMContentLoaded",(function(){document.body.parentNode.classList.remove("no-js"),r=document.querySelector("header"),o=document.querySelector(".toc-scroll"),i()}))})()})(); +//# sourceMappingURL=qiskit-sphinx-theme.js.map \ No newline at end of file diff --git a/stable/0.4/_static/scripts/qiskit-sphinx-theme.js.LICENSE.txt b/stable/0.4/_static/scripts/qiskit-sphinx-theme.js.LICENSE.txt new file mode 100644 index 000000000..bbd6389ef --- /dev/null +++ b/stable/0.4/_static/scripts/qiskit-sphinx-theme.js.LICENSE.txt @@ -0,0 +1,9 @@ +/*! + * gumshoejs v5.1.2 (patched by @pradyunsg) + * A simple, framework-agnostic scrollspy script. + * (c) 2019 Chris Ferdinandi + * MIT License + * http://github.com/cferdinandi/gumshoe + */ + +/*! This file is vendored from Furo (created by Pradyun Gedam) and used under the MIT license. */ diff --git a/stable/0.4/_static/scripts/qiskit-sphinx-theme.js.map b/stable/0.4/_static/scripts/qiskit-sphinx-theme.js.map new file mode 100644 index 000000000..f9964313f --- /dev/null +++ b/stable/0.4/_static/scripts/qiskit-sphinx-theme.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/qiskit-sphinx-theme.js","mappings":";iCAAA,MASWA,SAWS,IAAX,EAAAC,EACH,EAAAA,EACkB,oBAAXC,OACPA,OACAC,KAbS,EAAF,WACP,OAaJ,SAAUD,GACR,aAMA,IAAIE,EAAW,CAEbC,SAAU,SACVC,aAAc,SAGdC,QAAQ,EACRC,YAAa,SAGbC,OAAQ,EACRC,QAAQ,EAGRC,QAAQ,GA6BNC,EAAY,SAAUC,EAAMC,EAAMC,GAEpC,GAAKA,EAAOC,SAASL,OAArB,CAGA,IAAIM,EAAQ,IAAIC,YAAYL,EAAM,CAChCM,SAAS,EACTC,YAAY,EACZL,OAAQA,IAIVD,EAAKO,cAAcJ,EAVgB,CAWrC,EAOIK,EAAe,SAAUR,GAC3B,IAAIS,EAAW,EACf,GAAIT,EAAKU,aACP,KAAOV,GACLS,GAAYT,EAAKW,UACjBX,EAAOA,EAAKU,aAGhB,OAAOD,GAAY,EAAIA,EAAW,CACpC,EAMIG,EAAe,SAAUC,GACvBA,GACFA,EAASC,MAAK,SAAUC,EAAOC,GAG7B,OAFcR,EAAaO,EAAME,SACnBT,EAAaQ,EAAMC,UACF,EACxB,CACT,GAEJ,EAwCIC,EAAW,SAAUlB,EAAME,EAAUiB,GACvC,IAAIC,EAASpB,EAAKqB,wBACd1B,EAnCU,SAAUO,GAExB,MAA+B,mBAApBA,EAASP,OACX2B,WAAWpB,EAASP,UAItB2B,WAAWpB,EAASP,OAC7B,CA2Be4B,CAAUrB,GACvB,OAAIiB,EAEAK,SAASJ,EAAOD,OAAQ,KACvB/B,EAAOqC,aAAeC,SAASC,gBAAgBC,cAG7CJ,SAASJ,EAAOS,IAAK,KAAOlC,CACrC,EAMImC,EAAa,WACf,OACEC,KAAKC,KAAK5C,EAAOqC,YAAcrC,EAAO6C,cAnCjCF,KAAKG,IACVR,SAASS,KAAKC,aACdV,SAASC,gBAAgBS,aACzBV,SAASS,KAAKE,aACdX,SAASC,gBAAgBU,aACzBX,SAASS,KAAKP,aACdF,SAASC,gBAAgBC,aAkC7B,EAmBIU,EAAY,SAAUzB,EAAUX,GAClC,IAAIqC,EAAO1B,EAASA,EAAS2B,OAAS,GACtC,GAbgB,SAAUC,EAAMvC,GAChC,SAAI4B,MAAgBZ,EAASuB,EAAKxB,QAASf,GAAU,GAEvD,CAUMwC,CAAYH,EAAMrC,GAAW,OAAOqC,EACxC,IAAK,IAAII,EAAI9B,EAAS2B,OAAS,EAAGG,GAAK,EAAGA,IACxC,GAAIzB,EAASL,EAAS8B,GAAG1B,QAASf,GAAW,OAAOW,EAAS8B,EAEjE,EAOIC,EAAmB,SAAUC,EAAK3C,GAEpC,GAAKA,EAAST,QAAWoD,EAAIC,WAA7B,CAGA,IAAIC,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASR,aAG7BkD,EAAiBG,EAAI7C,GAV0B,CAWjD,EAOIiD,EAAa,SAAUC,EAAOlD,GAEhC,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASX,UAC7B6D,EAAMnC,QAAQgC,UAAUC,OAAOhD,EAASV,cAGxCoD,EAAiBG,EAAI7C,GAGrBJ,EAAU,oBAAqBiD,EAAI,CACjCM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,EAOIoD,EAAiB,SAAUT,EAAK3C,GAElC,GAAKA,EAAST,OAAd,CAGA,IAAIsD,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASR,aAG1B4D,EAAeP,EAAI7C,GAVS,CAW9B,EA6LA,OA1JkB,SAAUsD,EAAUC,GAKpC,IACIC,EAAU7C,EAAU8C,EAASC,EAAS1D,EADtC2D,EAAa,CAUjBA,MAAmB,WAEjBH,EAAWhC,SAASoC,iBAAiBN,GAGrC3C,EAAW,GAGXkD,MAAMC,UAAUC,QAAQC,KAAKR,GAAU,SAAUjB,GAE/C,IAAIxB,EAAUS,SAASyC,eACrBC,mBAAmB3B,EAAK4B,KAAKC,OAAO,KAEjCrD,GAGLJ,EAAS0D,KAAK,CACZ1B,IAAKJ,EACLxB,QAASA,GAEb,IAGAL,EAAaC,EACf,EAKAgD,OAAoB,WAElB,IAAIW,EAASlC,EAAUzB,EAAUX,GAG5BsE,EASDb,GAAWa,EAAOvD,UAAY0C,EAAQ1C,UAG1CkC,EAAWQ,EAASzD,GAzFT,SAAUkD,EAAOlD,GAE9B,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASX,UAC1B6D,EAAMnC,QAAQgC,UAAUM,IAAIrD,EAASV,cAGrC8D,EAAeP,EAAI7C,GAGnBJ,EAAU,kBAAmBiD,EAAI,CAC/BM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,CAqEIuE,CAASD,EAAQtE,GAGjByD,EAAUa,GAfJb,IACFR,EAAWQ,EAASzD,GACpByD,EAAU,KAchB,GAMIe,EAAgB,SAAUvE,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,sBAAsBf,EAAWgB,OACpD,EAMIC,EAAgB,SAAU3E,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,uBAAsB,WACrChE,EAAaC,GACbgD,EAAWgB,QACb,GACF,EAkDA,OA7CAhB,EAAWkB,QAAU,WAEfpB,GACFR,EAAWQ,EAASzD,GAItBd,EAAO4F,oBAAoB,SAAUN,GAAe,GAChDxE,EAASN,QACXR,EAAO4F,oBAAoB,SAAUF,GAAe,GAItDjE,EAAW,KACX6C,EAAW,KACXC,EAAU,KACVC,EAAU,KACV1D,EAAW,IACb,EAOEA,EA3XS,WACX,IAAI+E,EAAS,CAAC,EAOd,OANAlB,MAAMC,UAAUC,QAAQC,KAAKgB,WAAW,SAAUC,GAChD,IAAK,IAAIC,KAAOD,EAAK,CACnB,IAAKA,EAAIE,eAAeD,GAAM,OAC9BH,EAAOG,GAAOD,EAAIC,EACpB,CACF,IACOH,CACT,CAkXeK,CAAOhG,EAAUmE,GAAW,CAAC,GAGxCI,EAAW0B,QAGX1B,EAAWgB,SAGXzF,EAAOoG,iBAAiB,SAAUd,GAAe,GAC7CxE,EAASN,QACXR,EAAOoG,iBAAiB,SAAUV,GAAe,GAS9CjB,CACT,CAOF,CArcW4B,CAAQvG,EAChB,UAFM,SAEN,uBCZDwG,EAA2B,CAAC,EAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaE,QAGrB,IAAIC,EAASN,EAAyBE,GAAY,CAGjDG,QAAS,CAAC,GAOX,OAHAE,EAAoBL,GAAU1B,KAAK8B,EAAOD,QAASC,EAAQA,EAAOD,QAASJ,GAGpEK,EAAOD,OACf,CCrBAJ,EAAoBO,EAAKF,IACxB,IAAIG,EAASH,GAAUA,EAAOI,WAC7B,IAAOJ,EAAiB,QACxB,IAAM,EAEP,OADAL,EAAoBU,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdR,EAAoBU,EAAI,CAACN,EAASQ,KACjC,IAAI,IAAInB,KAAOmB,EACXZ,EAAoBa,EAAED,EAAYnB,KAASO,EAAoBa,EAAET,EAASX,IAC5EqB,OAAOC,eAAeX,EAASX,EAAK,CAAEuB,YAAY,EAAMC,IAAKL,EAAWnB,IAE1E,ECNDO,EAAoBxG,EAAI,WACvB,GAA0B,iBAAf0H,WAAyB,OAAOA,WAC3C,IACC,OAAOxH,MAAQ,IAAIyH,SAAS,cAAb,EAChB,CAAE,MAAOC,GACR,GAAsB,iBAAX3H,OAAqB,OAAOA,MACxC,CACA,CAPuB,GCAxBuG,EAAoBa,EAAI,CAACrB,EAAK6B,IAAUP,OAAOzC,UAAUqB,eAAenB,KAAKiB,EAAK6B,4CCQ9EC,EAAY,KACZC,EAAS,KACTC,EAAgB/H,OAAO6C,aAAeP,SAASC,gBAAgByF,UACnE,MAAMC,EAAmB,GA2EzB,SAASC,IACP,MAAMC,EAAeC,aAAaC,QAAQ,UAAY,OAZxD,IAAkBC,EACH,WADGA,EAaItI,OAAOuI,WAAW,gCAAgCC,QAI/C,SAAjBL,EACO,QACgB,SAAhBA,EACA,OAEA,OAIU,SAAjBA,EACO,OACgB,QAAhBA,EACA,QAEA,SA9BoB,SAATG,GAA4B,SAATA,IACzCG,QAAQC,MAAM,2BAA2BJ,yBACzCA,EAAO,QAGThG,SAASS,KAAK4F,QAAQC,MAAQN,EAC9BF,aAAaS,QAAQ,QAASP,GAC9BG,QAAQK,IAAI,cAAcR,UA0B5B,CAyDA,SAASnC,KART,WAEE,MAAM4C,EAAUzG,SAAS0G,uBAAuB,gBAChDrE,MAAMsE,KAAKF,GAASlE,SAASqE,IAC3BA,EAAI9C,iBAAiB,QAAS8B,EAAe,GAEjD,CAGEiB,GArDF,WAEE,IAAIC,EAA6B,EAC7BC,GAAU,EAEdrJ,OAAOoG,iBAAiB,UAAU,SAAUuB,GAC1CyB,EAA6BpJ,OAAOsJ,QAE/BD,IACHrJ,OAAOwF,uBAAsB,WAzDnC,IAAuB+D,IA0DDH,EA9GkC,GAAlDzG,KAAK6G,MAAM1B,EAAO7F,wBAAwBQ,KAC5CqF,EAAOjE,UAAUM,IAAI,YAErB2D,EAAOjE,UAAUC,OAAO,YAI5B,SAAmCyF,GAC7BA,EAAYtB,EACd3F,SAASC,gBAAgBsB,UAAUC,OAAO,oBAEtCyF,EAAYxB,EACdzF,SAASC,gBAAgBsB,UAAUM,IAAI,oBAC9BoF,EAAYxB,GACrBzF,SAASC,gBAAgBsB,UAAUC,OAAO,oBAG9CiE,EAAgBwB,CAClB,CAoCEE,CAA0BF,GAlC5B,SAA6BA,GACT,OAAd1B,IAKa,GAAb0B,EACF1B,EAAU6B,SAAS,EAAG,GAGtB/G,KAAKC,KAAK2G,IACV5G,KAAK6G,MAAMlH,SAASC,gBAAgBS,aAAehD,OAAOqC,aAE1DwF,EAAU6B,SAAS,EAAG7B,EAAU7E,cAGhBV,SAASqH,cAAc,mBAc3C,CAKEC,CAAoBL,GAwDdF,GAAU,CACZ,IAEAA,GAAU,EAEd,IACArJ,OAAO6J,QACT,CAoCEC,GAjCkB,OAAdjC,GAKJ,IAAI,IAAJ,CAAY,cAAe,CACzBrH,QAAQ,EACRuJ,WAAW,EACX5J,SAAU,iBACVI,OAAQ,KACN,IAAIyJ,EAAM9H,WAAW+H,iBAAiB3H,SAASC,iBAAiB2H,UAGhE,MAAMC,EAAY7H,SAASqH,cAAc,mBACnCS,EAAmBD,GAAqD,SAAxCF,iBAAiBE,GAAWE,QAC9D,IAAML,EACN,EACJ,OAAOlC,EAAO7F,wBAAwBqI,OAAU,GAAMN,EAAO,EAAII,CAAe,GAkBtF,CAcA9H,SAAS8D,iBAAiB,oBAT1B,WACE9D,SAASS,KAAKW,WAAWG,UAAUC,OAAO,SAE1CgE,EAASxF,SAASqH,cAAc,UAChC9B,EAAYvF,SAASqH,cAAc,eAEnCxD,GACF","sources":["webpack://qiskit_sphinx_theme/./src/qiskit_sphinx_theme/assets/scripts/gumshoe-patched.js","webpack://qiskit_sphinx_theme/webpack/bootstrap","webpack://qiskit_sphinx_theme/webpack/runtime/compat get default export","webpack://qiskit_sphinx_theme/webpack/runtime/define property getters","webpack://qiskit_sphinx_theme/webpack/runtime/global","webpack://qiskit_sphinx_theme/webpack/runtime/hasOwnProperty shorthand","webpack://qiskit_sphinx_theme/./src/qiskit_sphinx_theme/assets/scripts/qiskit-sphinx-theme.js"],"sourcesContent":["/*! This file is vendored from Furo (created by Pradyun Gedam) and used under the MIT license. */\n/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight,\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1)),\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n },\n);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\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__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","/*! This file is vendored from Furo (created by Pradyun Gedam) and used under the MIT license. */\n// When making changes, surround it with `QISKIT CHANGE: start` and `QISKIT CHANGE: end` comments.\n\nimport Gumshoe from \"./gumshoe-patched.js\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\nvar lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;\nconst GO_TO_TOP_OFFSET = 64;\n\nfunction scrollHandlerForHeader() {\n if (Math.floor(header.getBoundingClientRect().top) == 0) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForBackToTop(positionY) {\n if (positionY < GO_TO_TOP_OFFSET) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n } else {\n if (positionY < lastScrollTop) {\n document.documentElement.classList.add(\"show-back-to-top\");\n } else if (positionY > lastScrollTop) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n }\n }\n lastScrollTop = positionY;\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader();\n scrollHandlerForBackToTop(positionY);\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n let last_known_scroll_position = 0;\n let ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n offset: () => {\n let rem = parseFloat(getComputedStyle(document.documentElement).fontSize);\n // QISKIT CHANGE: start. Add 3.5rem for the Qiskit top nav bar, if visible.\n // See _top-nav-bar.scss for where the value comes from.\n const topNavBar = document.querySelector('qiskit-ui-shell');\n const topNavBarHeight = (topNavBar && getComputedStyle(topNavBar).display !== 'none')\n ? 3.5 * rem\n : 0;\n return header.getBoundingClientRect().height + (0.5 * rem) + 1 + topNavBarHeight;\n // QISKIT CHANGE: end.\n },\n });\n}\n\nfunction setupTheme() {\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"],"names":["root","g","window","this","defaults","navClass","contentClass","nested","nestedClass","offset","reflow","events","emitEvent","type","elem","detail","settings","event","CustomEvent","bubbles","cancelable","dispatchEvent","getOffsetTop","location","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","bottom","bounds","getBoundingClientRect","parseFloat","getOffset","parseInt","innerHeight","document","documentElement","clientHeight","top","isAtBottom","Math","ceil","pageYOffset","max","body","scrollHeight","offsetHeight","getActive","last","length","item","useLastItem","i","deactivateNested","nav","parentNode","li","closest","classList","remove","deactivate","items","link","activateNested","add","selector","options","navItems","current","timeout","publicAPIs","querySelectorAll","Array","prototype","forEach","call","getElementById","decodeURIComponent","hash","substr","push","active","activate","scrollHandler","cancelAnimationFrame","requestAnimationFrame","detect","resizeHandler","destroy","removeEventListener","merged","arguments","obj","key","hasOwnProperty","extend","setup","addEventListener","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","n","getter","__esModule","d","a","definition","o","Object","defineProperty","enumerable","get","globalThis","Function","e","prop","tocScroll","header","lastScrollTop","scrollTop","GO_TO_TOP_OFFSET","cycleThemeOnce","currentTheme","localStorage","getItem","mode","matchMedia","matches","console","error","dataset","theme","setItem","log","buttons","getElementsByClassName","from","btn","setupTheme","last_known_scroll_position","ticking","scrollY","positionY","floor","scrollHandlerForBackToTop","scrollTo","querySelector","scrollHandlerForTOC","scroll","setupScrollHandler","recursive","rem","getComputedStyle","fontSize","topNavBar","topNavBarHeight","display","height"],"sourceRoot":""} \ No newline at end of file diff --git a/stable/0.4/_static/searchtools.js b/stable/0.4/_static/searchtools.js new file mode 100644 index 000000000..7918c3fab --- /dev/null +++ b/stable/0.4/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/stable/0.4/_static/skeleton.css b/stable/0.4/_static/skeleton.css new file mode 100644 index 000000000..467c878c6 --- /dev/null +++ b/stable/0.4/_static/skeleton.css @@ -0,0 +1,296 @@ +/* Some sane resets. */ +html { + height: 100%; +} + +body { + margin: 0; + min-height: 100%; +} + +/* All the flexbox magic! */ +body, +.sb-announcement, +.sb-content, +.sb-main, +.sb-container, +.sb-container__inner, +.sb-article-container, +.sb-footer-content, +.sb-header, +.sb-header-secondary, +.sb-footer { + display: flex; +} + +/* These order things vertically */ +body, +.sb-main, +.sb-article-container { + flex-direction: column; +} + +/* Put elements in the center */ +.sb-header, +.sb-header-secondary, +.sb-container, +.sb-content, +.sb-footer, +.sb-footer-content { + justify-content: center; +} +/* Put elements at the ends */ +.sb-article-container { + justify-content: space-between; +} + +/* These elements grow. */ +.sb-main, +.sb-content, +.sb-container, +article { + flex-grow: 1; +} + +/* Because padding making this wider is not fun */ +article { + box-sizing: border-box; +} + +/* The announcements element should never be wider than the page. */ +.sb-announcement { + max-width: 100%; +} + +.sb-sidebar-primary, +.sb-sidebar-secondary { + flex-shrink: 0; + width: 17rem; +} + +.sb-announcement__inner { + justify-content: center; + + box-sizing: border-box; + height: 3rem; + + overflow-x: auto; + white-space: nowrap; +} + +/* Sidebars, with checkbox-based toggle */ +.sb-sidebar-primary, +.sb-sidebar-secondary { + position: fixed; + height: 100%; + top: 0; +} + +.sb-sidebar-primary { + left: -17rem; + transition: left 250ms ease-in-out; +} +.sb-sidebar-secondary { + right: -17rem; + transition: right 250ms ease-in-out; +} + +.sb-sidebar-toggle { + display: none; +} +.sb-sidebar-overlay { + position: fixed; + top: 0; + width: 0; + height: 0; + + transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease; + + opacity: 0; + background-color: rgba(0, 0, 0, 0.54); +} + +#sb-sidebar-toggle--primary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"], +#sb-sidebar-toggle--secondary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] { + width: 100%; + height: 100%; + opacity: 1; + transition: width 0ms ease, height 0ms ease, opacity 250ms ease; +} + +#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary { + left: 0; +} +#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary { + right: 0; +} + +/* Full-width mode */ +.drop-secondary-sidebar-for-full-width-content + .hide-when-secondary-sidebar-shown { + display: none !important; +} +.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary { + display: none !important; +} + +/* Mobile views */ +.sb-page-width { + width: 100%; +} + +.sb-article-container, +.sb-footer-content__inner, +.drop-secondary-sidebar-for-full-width-content .sb-article, +.drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 100vw; +} + +.sb-article, +.match-content-width { + padding: 0 1rem; + box-sizing: border-box; +} + +@media (min-width: 32rem) { + .sb-article, + .match-content-width { + padding: 0 2rem; + } +} + +/* Tablet views */ +@media (min-width: 42rem) { + .sb-article-container { + width: auto; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 42rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 46rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 46rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 50rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 50rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Tablet views */ +@media (min-width: 59rem) { + .sb-sidebar-secondary { + position: static; + } + .hide-when-secondary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 63rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 67rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Desktop views */ +@media (min-width: 76rem) { + .sb-sidebar-primary { + position: static; + } + .hide-when-primary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} + +/* Full desktop views */ +@media (min-width: 80rem) { + .sb-article, + .match-content-width { + width: 46rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } +} + +@media (min-width: 84rem) { + .sb-article, + .match-content-width { + width: 50rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } +} + +@media (min-width: 88rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-page-width { + width: 88rem; + } +} diff --git a/stable/0.4/_static/sphinx_highlight.js b/stable/0.4/_static/sphinx_highlight.js new file mode 100644 index 000000000..8a96c69a1 --- /dev/null +++ b/stable/0.4/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!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.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/stable/0.4/_static/styles/furo-extensions.css b/stable/0.4/_static/styles/furo-extensions.css new file mode 100644 index 000000000..bc447f228 --- /dev/null +++ b/stable/0.4/_static/styles/furo-extensions.css @@ -0,0 +1,2 @@ +#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;opacity:1;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0ms}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)} +/*# sourceMappingURL=furo-extensions.css.map*/ \ No newline at end of file diff --git a/stable/0.4/_static/styles/furo-extensions.css.map b/stable/0.4/_static/styles/furo-extensions.css.map new file mode 100644 index 000000000..9ba5637f9 --- /dev/null +++ b/stable/0.4/_static/styles/furo-extensions.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAKE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cALA,UASA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,oBACA,CACA,wCACE,cAEJ,8BACE,UC5CN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Make it visible\n opacity: 1\n\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/stable/0.4/_static/styles/furo.css b/stable/0.4/_static/styles/furo.css new file mode 100644 index 000000000..3d29a218f --- /dev/null +++ b/stable/0.4/_static/styles/furo.css @@ -0,0 +1,2 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@media print{.content-icon-container,.headerlink,.mobile-header,.related-pages{display:none!important}.highlight{border:.1pt solid var(--color-foreground-border)}a,blockquote,dl,ol,pre,table,ul{page-break-inside:avoid}caption,figure,h1,h2,h3,h4,h5,h6,img{page-break-after:avoid;page-break-inside:avoid}dl,ol,ul{page-break-before:avoid}}.visually-hidden{clip:rect(0,0,0,0)!important;border:0!important;height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}:-moz-focusring{outline:auto}body{--font-stack:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;--font-stack--monospace:"SFMono-Regular",Menlo,Consolas,Monaco,Liberation Mono,Lucida Console,monospace;--font-size--normal:100%;--font-size--small:87.5%;--font-size--small--2:81.25%;--font-size--small--3:75%;--font-size--small--4:62.5%;--sidebar-caption-font-size:var(--font-size--small--2);--sidebar-item-font-size:var(--font-size--small);--sidebar-search-input-font-size:var(--font-size--small);--toc-font-size:var(--font-size--small--3);--toc-font-size--mobile:var(--font-size--normal);--toc-title-font-size:var(--font-size--small--4);--admonition-font-size:0.8125rem;--admonition-title-font-size:0.8125rem;--code-font-size:var(--font-size--small--2);--api-font-size:var(--font-size--small);--header-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*4);--header-padding:0.5rem;--sidebar-tree-space-above:1.5rem;--sidebar-caption-space-above:1rem;--sidebar-item-line-height:1rem;--sidebar-item-spacing-vertical:0.5rem;--sidebar-item-spacing-horizontal:1rem;--sidebar-item-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*2);--sidebar-expander-width:var(--sidebar-item-height);--sidebar-search-space-above:0.5rem;--sidebar-search-input-spacing-vertical:0.5rem;--sidebar-search-input-spacing-horizontal:0.5rem;--sidebar-search-input-height:1rem;--sidebar-search-icon-size:var(--sidebar-search-input-height);--toc-title-padding:0.25rem 0;--toc-spacing-vertical:1.5rem;--toc-spacing-horizontal:1.5rem;--toc-item-spacing-vertical:0.4rem;--toc-item-spacing-horizontal:1rem;--icon-search:url('data:image/svg+xml;charset=utf-8,');--icon-pencil:url('data:image/svg+xml;charset=utf-8,');--icon-abstract:url('data:image/svg+xml;charset=utf-8,');--icon-info:url('data:image/svg+xml;charset=utf-8,');--icon-flame:url('data:image/svg+xml;charset=utf-8,');--icon-question:url('data:image/svg+xml;charset=utf-8,');--icon-warning:url('data:image/svg+xml;charset=utf-8,');--icon-failure:url('data:image/svg+xml;charset=utf-8,');--icon-spark:url('data:image/svg+xml;charset=utf-8,');--color-admonition-title--caution:#ff9100;--color-admonition-title-background--caution:rgba(255,145,0,.2);--color-admonition-title--warning:#ff9100;--color-admonition-title-background--warning:rgba(255,145,0,.2);--color-admonition-title--danger:#ff5252;--color-admonition-title-background--danger:rgba(255,82,82,.2);--color-admonition-title--attention:#ff5252;--color-admonition-title-background--attention:rgba(255,82,82,.2);--color-admonition-title--error:#ff5252;--color-admonition-title-background--error:rgba(255,82,82,.2);--color-admonition-title--hint:#00c852;--color-admonition-title-background--hint:rgba(0,200,82,.2);--color-admonition-title--tip:#00c852;--color-admonition-title-background--tip:rgba(0,200,82,.2);--color-admonition-title--important:#00bfa5;--color-admonition-title-background--important:rgba(0,191,165,.2);--color-admonition-title--note:#00b0ff;--color-admonition-title-background--note:rgba(0,176,255,.2);--color-admonition-title--seealso:#448aff;--color-admonition-title-background--seealso:rgba(68,138,255,.2);--color-admonition-title--admonition-todo:grey;--color-admonition-title-background--admonition-todo:hsla(0,0%,50%,.2);--color-admonition-title:#651fff;--color-admonition-title-background:rgba(101,31,255,.2);--icon-admonition-default:var(--icon-abstract);--color-topic-title:#14b8a6;--color-topic-title-background:rgba(20,184,166,.2);--icon-topic-default:var(--icon-pencil);--color-problematic:#b30000;--color-foreground-primary:#000;--color-foreground-secondary:#5a5c63;--color-foreground-muted:#646776;--color-foreground-border:#878787;--color-background-primary:#fff;--color-background-secondary:#f8f9fb;--color-background-hover:#efeff4;--color-background-hover--transparent:#efeff400;--color-background-border:#eeebee;--color-background-item:#ccc;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2962ff;--color-brand-content:#2a5adf;--color-api-background:var(--color-background-hover--transparent);--color-api-background-hover:var(--color-background-hover);--color-api-overall:var(--color-foreground-secondary);--color-api-name:var(--color-problematic);--color-api-pre-name:var(--color-problematic);--color-api-paren:var(--color-foreground-secondary);--color-api-keyword:var(--color-foreground-primary);--color-highlight-on-target:#ffc;--color-inline-code-background:var(--color-background-secondary);--color-highlighted-background:#def;--color-highlighted-text:var(--color-foreground-primary);--color-guilabel-background:#ddeeff80;--color-guilabel-border:#bedaf580;--color-guilabel-text:var(--color-foreground-primary);--color-admonition-background:transparent;--color-table-header-background:var(--color-background-secondary);--color-table-border:var(--color-background-border);--color-card-border:var(--color-background-secondary);--color-card-background:transparent;--color-card-marginals-background:var(--color-background-secondary);--color-header-background:var(--color-background-primary);--color-header-border:var(--color-background-border);--color-header-text:var(--color-foreground-primary);--color-sidebar-background:var(--color-background-secondary);--color-sidebar-background-border:var(--color-background-border);--color-sidebar-brand-text:var(--color-foreground-primary);--color-sidebar-caption-text:var(--color-foreground-muted);--color-sidebar-link-text:var(--color-foreground-secondary);--color-sidebar-link-text--top-level:var(--color-brand-primary);--color-sidebar-item-background:var(--color-sidebar-background);--color-sidebar-item-background--current:var( --color-sidebar-item-background );--color-sidebar-item-background--hover:linear-gradient(90deg,var(--color-background-hover--transparent) 0%,var(--color-background-hover) var(--sidebar-item-spacing-horizontal),var(--color-background-hover) 100%);--color-sidebar-item-expander-background:transparent;--color-sidebar-item-expander-background--hover:var( --color-background-hover );--color-sidebar-search-text:var(--color-foreground-primary);--color-sidebar-search-background:var(--color-background-secondary);--color-sidebar-search-background--focus:var(--color-background-primary);--color-sidebar-search-border:var(--color-background-border);--color-sidebar-search-icon:var(--color-foreground-muted);--color-toc-background:var(--color-background-primary);--color-toc-title-text:var(--color-foreground-muted);--color-toc-item-text:var(--color-foreground-secondary);--color-toc-item-text--hover:var(--color-foreground-primary);--color-toc-item-text--active:var(--color-brand-primary);--color-content-foreground:var(--color-foreground-primary);--color-content-background:transparent;--color-link:var(--color-brand-content);--color-link--hover:var(--color-brand-content);--color-link-underline:var(--color-background-border);--color-link-underline--hover:var(--color-foreground-border)}.only-light{display:block!important}html body .only-dark{display:none!important}@media not print{body[data-theme=dark]{--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body[data-theme=dark] .only-light{display:none!important}body[data-theme=dark] .only-dark{display:block!important}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body:not([data-theme=light]) .only-light{display:none!important}body:not([data-theme=light]) .only-dark{display:block!important}}}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto,body[data-theme=dark] .theme-toggle svg.theme-icon-when-dark,body[data-theme=light] .theme-toggle svg.theme-icon-when-light{display:block}body{font-family:var(--font-stack)}code,kbd,pre,samp{font-family:var(--font-stack--monospace)}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}article{line-height:1.5}h1,h2,h3,h4,h5,h6{border-radius:.5rem;font-weight:700;line-height:1.25;margin:.5rem -.5rem;padding-left:.5rem;padding-right:.5rem}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0}h1{font-size:2.5em;margin-bottom:1rem}h1,h2{margin-top:1.75rem}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.25em}h5{font-size:1.125em}h6{font-size:1em}small{font-size:80%;opacity:75%}p{margin-bottom:.75rem;margin-top:.5rem}hr.docutils{background-color:var(--color-background-border);border:0;height:1px;margin:2rem 0;padding:0}.centered{text-align:center}a{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}a:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link{color:inherit}a.muted-link:hover{color:var(--color-link);text-decoration-color:var(--color-link-underline--hover)}html{overflow-x:hidden;overflow-y:scroll;scroll-behavior:smooth}.sidebar-scroll,.toc-scroll,article[role=main] *{scrollbar-color:var(--color-foreground-border) transparent;scrollbar-width:thin}.sidebar-scroll::-webkit-scrollbar,.toc-scroll::-webkit-scrollbar,article[role=main] ::-webkit-scrollbar{height:.25rem;width:.25rem}.sidebar-scroll::-webkit-scrollbar-thumb,.toc-scroll::-webkit-scrollbar-thumb,article[role=main] ::-webkit-scrollbar-thumb{background-color:var(--color-foreground-border);border-radius:.125rem}body,html{background:var(--color-background-primary);color:var(--color-foreground-primary);height:100%}article{background:var(--color-content-background);color:var(--color-content-foreground);overflow-wrap:break-word}.page{display:flex;min-height:100%}.mobile-header{background-color:var(--color-header-background);border-bottom:1px solid var(--color-header-border);color:var(--color-header-text);display:none;height:var(--header-height);width:100%;z-index:10}.mobile-header.scrolled{border-bottom:none;box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2)}.mobile-header .header-center a{color:var(--color-header-text);text-decoration:none}.main{display:flex;flex:1}.sidebar-drawer{background:var(--color-sidebar-background);border-right:1px solid var(--color-sidebar-background-border);box-sizing:border-box;display:flex;justify-content:flex-end;min-width:15em;width:calc(50% - 26em)}.sidebar-container,.toc-drawer{box-sizing:border-box;width:15em}.toc-drawer{background:var(--color-toc-background);padding-right:1rem}.sidebar-sticky,.toc-sticky{display:flex;flex-direction:column;height:min(100%,100vh);height:100vh;position:sticky;top:0}.sidebar-scroll,.toc-scroll{flex-grow:1;flex-shrink:1;overflow:auto;scroll-behavior:smooth}.content{display:flex;flex-direction:column;justify-content:space-between;padding:0 3em;width:46em}.icon{display:inline-block;height:1rem;width:1rem}.icon svg{height:100%;width:100%}.announcement{align-items:center;background-color:var(--color-announcement-background);color:var(--color-announcement-text);display:flex;height:var(--header-height);overflow-x:auto}.announcement+.page{min-height:calc(100% - var(--header-height))}.announcement-content{box-sizing:border-box;min-width:100%;padding:.5rem;text-align:center;white-space:nowrap}.announcement-content a{color:var(--color-announcement-text);text-decoration-color:var(--color-announcement-text)}.announcement-content a:hover{color:var(--color-announcement-text);text-decoration-color:var(--color-link--hover)}.no-js .theme-toggle-container{display:none}.theme-toggle-container{vertical-align:middle}.theme-toggle{background:transparent;border:none;cursor:pointer;padding:0}.theme-toggle svg{color:var(--color-foreground-primary);display:none;height:1rem;vertical-align:middle;width:1rem}.theme-toggle-header{float:left;padding:1rem .5rem}.nav-overlay-icon,.toc-overlay-icon{cursor:pointer;display:none}.nav-overlay-icon .icon,.toc-overlay-icon .icon{color:var(--color-foreground-secondary);height:1rem;width:1rem}.nav-overlay-icon,.toc-header-icon{align-items:center;justify-content:center}.toc-content-icon{height:1.5rem;width:1.5rem}.content-icon-container{display:flex;float:right;gap:.5rem;margin-bottom:1rem;margin-left:1rem;margin-top:1.5rem}.content-icon-container .edit-this-page svg{color:inherit;height:1rem;width:1rem}.sidebar-toggle{display:none;position:absolute}.sidebar-toggle[name=__toc]{left:20px}.sidebar-toggle:checked{left:40px}.overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0ms,height 0ms,opacity .25s ease-out;width:0}.sidebar-overlay{z-index:20}.toc-overlay{z-index:40}.sidebar-drawer{transition:left .25s ease-in-out;z-index:30}.toc-drawer{transition:right .25s ease-in-out;z-index:50}#__navigation:checked~.sidebar-overlay{height:100%;opacity:1;width:100%}#__navigation:checked~.page .sidebar-drawer{left:0;top:0}#__toc:checked~.toc-overlay{height:100%;opacity:1;width:100%}#__toc:checked~.page .toc-drawer{right:0;top:0}.back-to-top{background:var(--color-background-primary);border-radius:1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 1px 0 hsla(220,9%,46%,.502);display:none;font-size:.8125rem;left:0;margin-left:50%;padding:.5rem .75rem .5rem .5rem;position:fixed;text-decoration:none;top:1rem;transform:translateX(-50%);z-index:10}.back-to-top svg{fill:currentColor;display:inline-block;height:1rem;width:1rem}.back-to-top span{margin-left:.25rem}.show-back-to-top .back-to-top{align-items:center;display:flex}@media(min-width:97em){html{font-size:110%}}@media(max-width:82em){.toc-content-icon{display:flex}.toc-drawer{border-left:1px solid var(--color-background-muted);height:100vh;position:fixed;right:-15em;top:0}.toc-tree{border-left:none;font-size:var(--toc-font-size--mobile)}.sidebar-drawer{width:calc(50% - 18.5em)}}@media(max-width:67em){.nav-overlay-icon{display:flex}.sidebar-drawer{height:100vh;left:-15em;position:fixed;top:0;width:15em}.toc-header-icon{display:flex}.theme-toggle-content,.toc-content-icon{display:none}.theme-toggle-header{display:block}.mobile-header{align-items:center;display:flex;justify-content:space-between;position:sticky;top:0}.mobile-header .header-left,.mobile-header .header-right{display:flex;height:var(--header-height);padding:0 var(--header-padding)}.mobile-header .header-left label,.mobile-header .header-right label{height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.nav-overlay-icon .icon,.theme-toggle svg{height:1.25rem;width:1.25rem}:target{scroll-margin-top:var(--header-height)}.back-to-top{top:calc(var(--header-height) + .5rem)}.page{flex-direction:column;justify-content:center}.content{margin-left:auto;margin-right:auto}}@media(max-width:52em){.content{overflow-x:auto;width:100%}}@media(max-width:46em){.content{padding:0 1em}article aside.sidebar{float:none;margin:1rem 0;width:100%}}.admonition,.topic{background:var(--color-admonition-background);border-radius:.2rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1);font-size:var(--admonition-font-size);margin:1rem auto;overflow:hidden;padding:0 .5rem .5rem;page-break-inside:avoid}.admonition>:nth-child(2),.topic>:nth-child(2){margin-top:0}.admonition>:last-child,.topic>:last-child{margin-bottom:0}.admonition p.admonition-title,p.topic-title{font-size:var(--admonition-title-font-size);font-weight:500;line-height:1.3;margin:0 -.5rem .5rem;padding:.4rem .5rem .4rem 2rem;position:relative}.admonition p.admonition-title:before,p.topic-title:before{content:"";height:1rem;left:.5rem;position:absolute;width:1rem}p.admonition-title{background-color:var(--color-admonition-title-background)}p.admonition-title:before{background-color:var(--color-admonition-title);-webkit-mask-image:var(--icon-admonition-default);mask-image:var(--icon-admonition-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}p.topic-title{background-color:var(--color-topic-title-background)}p.topic-title:before{background-color:var(--color-topic-title);-webkit-mask-image:var(--icon-topic-default);mask-image:var(--icon-topic-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.admonition{border-left:.2rem solid var(--color-admonition-title)}.admonition.caution{border-left-color:var(--color-admonition-title--caution)}.admonition.caution>.admonition-title{background-color:var(--color-admonition-title-background--caution)}.admonition.caution>.admonition-title:before{background-color:var(--color-admonition-title--caution);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.warning{border-left-color:var(--color-admonition-title--warning)}.admonition.warning>.admonition-title{background-color:var(--color-admonition-title-background--warning)}.admonition.warning>.admonition-title:before{background-color:var(--color-admonition-title--warning);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.danger{border-left-color:var(--color-admonition-title--danger)}.admonition.danger>.admonition-title{background-color:var(--color-admonition-title-background--danger)}.admonition.danger>.admonition-title:before{background-color:var(--color-admonition-title--danger);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.attention{border-left-color:var(--color-admonition-title--attention)}.admonition.attention>.admonition-title{background-color:var(--color-admonition-title-background--attention)}.admonition.attention>.admonition-title:before{background-color:var(--color-admonition-title--attention);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.error{border-left-color:var(--color-admonition-title--error)}.admonition.error>.admonition-title{background-color:var(--color-admonition-title-background--error)}.admonition.error>.admonition-title:before{background-color:var(--color-admonition-title--error);-webkit-mask-image:var(--icon-failure);mask-image:var(--icon-failure)}.admonition.hint{border-left-color:var(--color-admonition-title--hint)}.admonition.hint>.admonition-title{background-color:var(--color-admonition-title-background--hint)}.admonition.hint>.admonition-title:before{background-color:var(--color-admonition-title--hint);-webkit-mask-image:var(--icon-question);mask-image:var(--icon-question)}.admonition.tip{border-left-color:var(--color-admonition-title--tip)}.admonition.tip>.admonition-title{background-color:var(--color-admonition-title-background--tip)}.admonition.tip>.admonition-title:before{background-color:var(--color-admonition-title--tip);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.important{border-left-color:var(--color-admonition-title--important)}.admonition.important>.admonition-title{background-color:var(--color-admonition-title-background--important)}.admonition.important>.admonition-title:before{background-color:var(--color-admonition-title--important);-webkit-mask-image:var(--icon-flame);mask-image:var(--icon-flame)}.admonition.note{border-left-color:var(--color-admonition-title--note)}.admonition.note>.admonition-title{background-color:var(--color-admonition-title-background--note)}.admonition.note>.admonition-title:before{background-color:var(--color-admonition-title--note);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition.seealso{border-left-color:var(--color-admonition-title--seealso)}.admonition.seealso>.admonition-title{background-color:var(--color-admonition-title-background--seealso)}.admonition.seealso>.admonition-title:before{background-color:var(--color-admonition-title--seealso);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.admonition-todo{border-left-color:var(--color-admonition-title--admonition-todo)}.admonition.admonition-todo>.admonition-title{background-color:var(--color-admonition-title-background--admonition-todo)}.admonition.admonition-todo>.admonition-title:before{background-color:var(--color-admonition-title--admonition-todo);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition-todo>.admonition-title{text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd{margin-left:2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:first-child{margin-top:.125rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list,dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:last-child{margin-bottom:.75rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list>dt{font-size:var(--font-size--small);text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd:empty{margin-bottom:.5rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul{margin-left:-1.2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p:nth-child(2){margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p+p:last-child:empty{margin-bottom:0;margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{color:var(--color-api-overall)}.sig:not(.sig-inline){background:var(--color-api-background);border-radius:.25rem;font-family:var(--font-stack--monospace);font-size:var(--api-font-size);font-weight:700;margin-left:-.25rem;margin-right:-.25rem;padding:.25rem .5rem .25rem 3em;text-indent:-2.5em;transition:background .1s ease-out}.sig:not(.sig-inline):hover{background:var(--color-api-background-hover)}.sig:not(.sig-inline) a.reference .viewcode-link{font-weight:400;width:3.5rem}em.property{font-style:normal}em.property:first-child{color:var(--color-api-keyword)}.sig-name{color:var(--color-api-name)}.sig-prename{color:var(--color-api-pre-name);font-weight:400}.sig-paren{color:var(--color-api-paren)}.sig-param{font-style:normal}.versionmodified{font-style:italic}div.deprecated p,div.versionadded p,div.versionchanged p{margin-bottom:.125rem;margin-top:.125rem}.viewcode-back,.viewcode-link{float:right;text-align:right}.line-block{margin-bottom:.75rem;margin-top:.5rem}.line-block .line-block{margin-bottom:0;margin-top:0;padding-left:1rem}.code-block-caption,article p.caption,table>caption{font-size:var(--font-size--small);text-align:center}.toctree-wrapper.compound .caption,.toctree-wrapper.compound :not(.caption)>.caption-text{font-size:var(--font-size--small);margin-bottom:0;text-align:initial;text-transform:uppercase}.toctree-wrapper.compound>ul{margin-bottom:0;margin-top:0}.sig-inline,code.literal{background:var(--color-inline-code-background);border-radius:.2em;font-size:var(--font-size--small--2);padding:.1em .2em}pre.literal-block .sig-inline,pre.literal-block code.literal{font-size:inherit;padding:0}p .sig-inline,p code.literal{border:1px solid var(--color-background-border)}.sig-inline{font-family:var(--font-stack--monospace)}div[class*=" highlight-"],div[class^=highlight-]{display:flex;margin:1em 0}div[class*=" highlight-"] .table-wrapper,div[class^=highlight-] .table-wrapper,pre{margin:0;padding:0}pre{overflow:auto}article[role=main] .highlight pre{line-height:1.5}.highlight pre,pre.literal-block{font-size:var(--code-font-size);padding:.625rem .875rem}pre.literal-block{background-color:var(--color-code-background);border-radius:.2rem;color:var(--color-code-foreground);margin-bottom:1rem;margin-top:1rem}.highlight{border-radius:.2rem;width:100%}.highlight .gp,.highlight span.linenos{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.highlight .hll{display:block;margin-left:-.875rem;margin-right:-.875rem;padding-left:.875rem;padding-right:.875rem}.code-block-caption{background-color:var(--color-code-background);border-bottom:1px solid;border-radius:.25rem;border-bottom-left-radius:0;border-bottom-right-radius:0;border-color:var(--color-background-border);color:var(--color-code-foreground);display:flex;font-weight:300;padding:.625rem .875rem}.code-block-caption+div[class]{margin-top:0}.code-block-caption+div[class] pre{border-top-left-radius:0;border-top-right-radius:0}.highlighttable{display:block;width:100%}.highlighttable tbody{display:block}.highlighttable tr{display:flex}.highlighttable td.linenos{background-color:var(--color-code-background);border-bottom-left-radius:.2rem;border-top-left-radius:.2rem;color:var(--color-code-foreground);padding:.625rem 0 .625rem .875rem}.highlighttable .linenodiv{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;font-size:var(--code-font-size);padding-right:.875rem}.highlighttable td.code{display:block;flex:1;overflow:hidden;padding:0}.highlighttable td.code .highlight{border-bottom-left-radius:0;border-top-left-radius:0}.highlight span.linenos{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;display:inline-block;margin-right:.875rem;padding-left:0;padding-right:.875rem}.footnote-reference{font-size:var(--font-size--small--4);vertical-align:super}dl.footnote.brackets{color:var(--color-foreground-secondary);display:grid;font-size:var(--font-size--small);grid-template-columns:max-content auto}dl.footnote.brackets dt{margin:0}dl.footnote.brackets dt>.fn-backref{margin-left:.25rem}dl.footnote.brackets dt:after{content:":"}dl.footnote.brackets dt .brackets:before{content:"["}dl.footnote.brackets dt .brackets:after{content:"]"}dl.footnote.brackets dd{margin:0;padding:0 1rem}aside.footnote{color:var(--color-foreground-secondary);font-size:var(--font-size--small)}aside.footnote>span,div.citation>span{float:left;font-weight:500;padding-right:.25rem}aside.footnote>p,div.citation>p{margin-left:2rem}img{box-sizing:border-box;height:auto;max-width:100%}article .figure,article figure{border-radius:.2rem;margin:0}article .figure :last-child,article figure :last-child{margin-bottom:0}article .align-left{clear:left;float:left;margin:0 1rem 1rem}article .align-right{clear:right;float:right;margin:0 1rem 1rem}article .align-center,article .align-default{display:block;margin-left:auto;margin-right:auto;text-align:center}article table.align-default{display:table;text-align:initial}.domainindex-jumpbox,.genindex-jumpbox{border-bottom:1px solid var(--color-background-border);border-top:1px solid var(--color-background-border);padding:.25rem}.domainindex-section h2,.genindex-section h2{margin-bottom:.5rem;margin-top:.75rem}.domainindex-section ul,.genindex-section ul{margin-bottom:0;margin-top:0}ol,ul{margin-bottom:1rem;margin-top:1rem;padding-left:1.2rem}ol li>p:first-child,ul li>p:first-child{margin-bottom:.25rem;margin-top:.25rem}ol li>p:last-child,ul li>p:last-child{margin-top:.25rem}ol li>ol,ol li>ul,ul li>ol,ul li>ul{margin-bottom:.5rem;margin-top:.5rem}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}.simple li>ol,.simple li>ul,.toctree-wrapper li>ol,.toctree-wrapper li>ul{margin-bottom:0;margin-top:0}.field-list dt,.option-list dt,dl.footnote dt,dl.glossary dt,dl.simple dt,dl:not([class]) dt{font-weight:500;margin-top:.25rem}.field-list dt+dt,.option-list dt+dt,dl.footnote dt+dt,dl.glossary dt+dt,dl.simple dt+dt,dl:not([class]) dt+dt{margin-top:0}.field-list dt .classifier:before,.option-list dt .classifier:before,dl.footnote dt .classifier:before,dl.glossary dt .classifier:before,dl.simple dt .classifier:before,dl:not([class]) dt .classifier:before{content:":";margin-left:.2rem;margin-right:.2rem}.field-list dd ul,.field-list dd>p:first-child,.option-list dd ul,.option-list dd>p:first-child,dl.footnote dd ul,dl.footnote dd>p:first-child,dl.glossary dd ul,dl.glossary dd>p:first-child,dl.simple dd ul,dl.simple dd>p:first-child,dl:not([class]) dd ul,dl:not([class]) dd>p:first-child{margin-top:.125rem}.field-list dd ul,.option-list dd ul,dl.footnote dd ul,dl.glossary dd ul,dl.simple dd ul,dl:not([class]) dd ul{margin-bottom:.125rem}.math-wrapper{overflow-x:auto;width:100%}div.math{position:relative;text-align:center}div.math .headerlink,div.math:focus .headerlink{display:none}div.math:hover .headerlink{display:inline-block}div.math span.eqno{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);z-index:1}abbr[title]{cursor:help}.problematic{color:var(--color-problematic)}kbd:not(.compound){background-color:var(--color-background-secondary);border:1px solid var(--color-foreground-border);border-radius:.2rem;box-shadow:0 .0625rem 0 rgba(0,0,0,.2),inset 0 0 0 .125rem var(--color-background-primary);color:var(--color-foreground-primary);display:inline-block;font-size:var(--font-size--small--3);margin:0 .2rem;padding:0 .2rem;vertical-align:text-bottom}blockquote{background:var(--color-background-secondary);border-left:4px solid var(--color-background-border);margin-left:0;margin-right:0;padding:.5rem 1rem}blockquote .attribution{font-weight:600;text-align:right}blockquote.highlights,blockquote.pull-quote{font-size:1.25em}blockquote.epigraph,blockquote.pull-quote{border-left-width:0;border-radius:.5rem}blockquote.highlights{background:transparent;border-left-width:0}p .reference img{vertical-align:middle}p.rubric{font-size:1.125em;font-weight:700;line-height:1.25}dd p.rubric{font-size:var(--font-size--small);font-weight:inherit;line-height:inherit;text-transform:uppercase}article .sidebar{background-color:var(--color-background-secondary);border:1px solid var(--color-background-border);border-radius:.2rem;clear:right;float:right;margin-left:1rem;margin-right:0;width:30%}article .sidebar>*{padding-left:1rem;padding-right:1rem}article .sidebar>ol,article .sidebar>ul{padding-left:2.2rem}article .sidebar .sidebar-title{border-bottom:1px solid var(--color-background-border);font-weight:500;margin:0;padding:.5rem 1rem}.table-wrapper{margin-bottom:.5rem;margin-top:1rem;overflow-x:auto;padding:.2rem .2rem .75rem;width:100%}table.docutils{border-collapse:collapse;border-radius:.2rem;border-spacing:0;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)}table.docutils th{background:var(--color-table-header-background)}table.docutils td,table.docutils th{border-bottom:1px solid var(--color-table-border);border-left:1px solid var(--color-table-border);border-right:1px solid var(--color-table-border);padding:0 .25rem}table.docutils td p,table.docutils th p{margin:.25rem}table.docutils td:first-child,table.docutils th:first-child{border-left:none}table.docutils td:last-child,table.docutils th:last-child{border-right:none}table.docutils td.text-left,table.docutils th.text-left{text-align:left}table.docutils td.text-right,table.docutils th.text-right{text-align:right}table.docutils td.text-center,table.docutils th.text-center{text-align:center}:target{scroll-margin-top:.5rem}@media(max-width:67em){:target{scroll-margin-top:calc(.5rem + var(--header-height))}section>span:target{scroll-margin-top:calc(.8rem + var(--header-height))}}.headerlink{font-weight:100;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-block-caption>.headerlink,dl dt>.headerlink,figcaption p>.headerlink,h1>.headerlink,h2>.headerlink,h3>.headerlink,h4>.headerlink,h5>.headerlink,h6>.headerlink,p.caption>.headerlink,table>caption>.headerlink{margin-left:.5rem;visibility:hidden}.code-block-caption:hover>.headerlink,dl dt:hover>.headerlink,figcaption p:hover>.headerlink,h1:hover>.headerlink,h2:hover>.headerlink,h3:hover>.headerlink,h4:hover>.headerlink,h5:hover>.headerlink,h6:hover>.headerlink,p.caption:hover>.headerlink,table>caption:hover>.headerlink{visibility:visible}.code-block-caption>.toc-backref,dl dt>.toc-backref,figcaption p>.toc-backref,h1>.toc-backref,h2>.toc-backref,h3>.toc-backref,h4>.toc-backref,h5>.toc-backref,h6>.toc-backref,p.caption>.toc-backref,table>caption>.toc-backref{color:inherit;text-decoration-line:none}figure:hover>figcaption>p>.headerlink,table:hover>caption>.headerlink{visibility:visible}:target>h1:first-of-type,:target>h2:first-of-type,:target>h3:first-of-type,:target>h4:first-of-type,:target>h5:first-of-type,:target>h6:first-of-type,span:target~h1:first-of-type,span:target~h2:first-of-type,span:target~h3:first-of-type,span:target~h4:first-of-type,span:target~h5:first-of-type,span:target~h6:first-of-type{background-color:var(--color-highlight-on-target)}:target>h1:first-of-type code.literal,:target>h2:first-of-type code.literal,:target>h3:first-of-type code.literal,:target>h4:first-of-type code.literal,:target>h5:first-of-type code.literal,:target>h6:first-of-type code.literal,span:target~h1:first-of-type code.literal,span:target~h2:first-of-type code.literal,span:target~h3:first-of-type code.literal,span:target~h4:first-of-type code.literal,span:target~h5:first-of-type code.literal,span:target~h6:first-of-type code.literal{background-color:transparent}.literal-block-wrapper:target .code-block-caption,.this-will-duplicate-information-and-it-is-still-useful-here li :target,figure:target,table:target>caption{background-color:var(--color-highlight-on-target)}dt:target{background-color:var(--color-highlight-on-target)!important}.footnote-reference:target,.footnote>dt:target+dd{background-color:var(--color-highlight-on-target)}.guilabel{background-color:var(--color-guilabel-background);border:1px solid var(--color-guilabel-border);border-radius:.5em;color:var(--color-guilabel-text);font-size:.9em;padding:0 .3em}footer{display:flex;flex-direction:column;font-size:var(--font-size--small);margin-top:2rem}.bottom-of-page{align-items:center;border-top:1px solid var(--color-background-border);color:var(--color-foreground-secondary);display:flex;justify-content:space-between;line-height:1.5;margin-top:1rem;padding-bottom:1rem;padding-top:1rem}@media(max-width:46em){.bottom-of-page{flex-direction:column-reverse;gap:.25rem;text-align:center}}.bottom-of-page .left-details{font-size:var(--font-size--small)}.bottom-of-page .right-details{display:flex;flex-direction:column;gap:.25rem;text-align:right}.bottom-of-page .icons{display:flex;font-size:1rem;gap:.25rem;justify-content:flex-end}.bottom-of-page .icons a{text-decoration:none}.bottom-of-page .icons img,.bottom-of-page .icons svg{font-size:1.125rem;height:1em;width:1em}.related-pages a{align-items:center;display:flex;text-decoration:none}.related-pages a:hover .page-info .title{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}.related-pages a svg.furo-related-icon,.related-pages a svg.furo-related-icon>use{color:var(--color-foreground-border);flex-shrink:0;height:.75rem;margin:0 .5rem;width:.75rem}.related-pages a.next-page{clear:right;float:right;max-width:50%;text-align:right}.related-pages a.prev-page{clear:left;float:left;max-width:50%}.related-pages a.prev-page svg{transform:rotate(180deg)}.page-info{display:flex;flex-direction:column;overflow-wrap:anywhere}.next-page .page-info{align-items:flex-end}.page-info .context{align-items:center;color:var(--color-foreground-muted);display:flex;font-size:var(--font-size--small);padding-bottom:.1rem;text-decoration:none}ul.search{list-style:none;padding-left:0}ul.search li{border-bottom:1px solid var(--color-background-border);padding:1rem 0}[role=main] .highlighted{background-color:var(--color-highlighted-background);color:var(--color-highlighted-text)}.sidebar-brand{display:flex;flex-direction:column;flex-shrink:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none}.sidebar-brand-text{color:var(--color-sidebar-brand-text);font-size:1.5rem;overflow-wrap:break-word}.sidebar-brand-text,.sidebar-logo-container{margin:var(--sidebar-item-spacing-vertical) 0}.sidebar-logo{display:block;margin:0 auto;max-width:100%}.sidebar-search-container{align-items:center;background:var(--color-sidebar-search-background);display:flex;margin-top:var(--sidebar-search-space-above);position:relative}.sidebar-search-container:focus-within,.sidebar-search-container:hover{background:var(--color-sidebar-search-background--focus)}.sidebar-search-container:before{background-color:var(--color-sidebar-search-icon);content:"";height:var(--sidebar-search-icon-size);left:var(--sidebar-item-spacing-horizontal);-webkit-mask-image:var(--icon-search);mask-image:var(--icon-search);position:absolute;width:var(--sidebar-search-icon-size)}.sidebar-search{background:transparent;border:none;border-bottom:1px solid var(--color-sidebar-search-border);border-top:1px solid var(--color-sidebar-search-border);box-sizing:border-box;color:var(--color-sidebar-search-foreground);padding:var(--sidebar-search-input-spacing-vertical) var(--sidebar-search-input-spacing-horizontal) var(--sidebar-search-input-spacing-vertical) calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size));width:100%;z-index:10}.sidebar-search:focus{outline:none}.sidebar-search::-moz-placeholder{font-size:var(--sidebar-search-input-font-size)}.sidebar-search::placeholder{font-size:var(--sidebar-search-input-font-size)}#searchbox .highlight-link{margin:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0;text-align:center}#searchbox .highlight-link a{color:var(--color-sidebar-search-icon);font-size:var(--font-size--small--2)}.sidebar-tree{font-size:var(--sidebar-item-font-size);margin-bottom:var(--sidebar-item-spacing-vertical);margin-top:var(--sidebar-tree-space-above)}.sidebar-tree ul{display:flex;flex-direction:column;list-style:none;margin-bottom:0;margin-top:0;padding:0}.sidebar-tree li{margin:0;position:relative}.sidebar-tree li>ul{margin-left:var(--sidebar-item-spacing-horizontal)}.sidebar-tree .icon,.sidebar-tree .reference{color:var(--color-sidebar-link-text)}.sidebar-tree .reference{box-sizing:border-box;display:inline-block;height:100%;line-height:var(--sidebar-item-line-height);overflow-wrap:anywhere;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none;width:100%}.sidebar-tree .reference:hover{background:var(--color-sidebar-item-background--hover)}.sidebar-tree .reference.external:after{color:var(--color-sidebar-link-text);content:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='12' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23607D8B' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0 0h24v24H0z' stroke='none'/%3E%3Cpath d='M11 7H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-5M10 14 20 4M15 4h5v5'/%3E%3C/svg%3E");margin:0 .25rem;vertical-align:middle}.sidebar-tree .current-page>.reference{font-weight:700}.sidebar-tree label{align-items:center;cursor:pointer;display:flex;height:var(--sidebar-item-height);justify-content:center;position:absolute;right:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:var(--sidebar-expander-width)}.sidebar-tree .caption,.sidebar-tree :not(.caption)>.caption-text{color:var(--color-sidebar-caption-text);font-size:var(--sidebar-caption-font-size);font-weight:700;margin:var(--sidebar-caption-space-above) 0 0 0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-transform:uppercase}.sidebar-tree li.has-children>.reference{padding-right:var(--sidebar-expander-width)}.sidebar-tree .toctree-l1>.reference,.sidebar-tree .toctree-l1>label .icon{color:var(--color-sidebar-link-text--top-level)}.sidebar-tree label{background:var(--color-sidebar-item-expander-background)}.sidebar-tree label:hover{background:var(--color-sidebar-item-expander-background--hover)}.sidebar-tree .current>.reference{background:var(--color-sidebar-item-background--current)}.sidebar-tree .current>.reference:hover{background:var(--color-sidebar-item-background--hover)}.toctree-checkbox{display:none;position:absolute}.toctree-checkbox~ul{display:none}.toctree-checkbox~label .icon svg{transform:rotate(90deg)}.toctree-checkbox:checked~ul{display:block}.toctree-checkbox:checked~label .icon svg{transform:rotate(-90deg)}.toc-title-container{padding:var(--toc-title-padding);padding-top:var(--toc-spacing-vertical)}.toc-title{color:var(--color-toc-title-text);font-size:var(--toc-title-font-size);padding-left:var(--toc-spacing-horizontal);text-transform:uppercase}.no-toc{display:none}.toc-tree-container{padding-bottom:var(--toc-spacing-vertical)}.toc-tree{border-left:1px solid var(--color-background-border);font-size:var(--toc-font-size);line-height:1.3;padding-left:calc(var(--toc-spacing-horizontal) - var(--toc-item-spacing-horizontal))}.toc-tree>ul>li:first-child{padding-top:0}.toc-tree>ul>li:first-child>ul{padding-left:0}.toc-tree>ul>li:first-child>a{display:none}.toc-tree ul{list-style-type:none;margin-bottom:0;margin-top:0;padding-left:var(--toc-item-spacing-horizontal)}.toc-tree li{padding-top:var(--toc-item-spacing-vertical)}.toc-tree li.scroll-current>.reference{color:var(--color-toc-item-text--active);font-weight:700}.toc-tree .reference{color:var(--color-toc-item-text);overflow-wrap:anywhere;text-decoration:none}.toc-scroll{max-height:100vh;overflow-y:scroll}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here){background:rgba(255,0,0,.25);color:var(--color-problematic)}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here):before{content:"ERROR: Adding a table of contents in Furo-based documentation is unnecessary, and does not work well with existing styling.Add a 'this-will-duplicate-information-and-it-is-still-useful-here' class, if you want an escape hatch."}.text-align\:left>p{text-align:left}.text-align\:center>p{text-align:center}.text-align\:right>p{text-align:right} +/*# sourceMappingURL=furo.css.map*/ \ No newline at end of file diff --git a/stable/0.4/_static/styles/furo.css.map b/stable/0.4/_static/styles/furo.css.map new file mode 100644 index 000000000..1924b3334 --- /dev/null +++ b/stable/0.4/_static/styles/furo.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo.css","mappings":"AAAA,2EAA2E,CAU3E,KAEE,6BAA8B,CAD9B,gBAEF,CASA,KACE,QACF,CAMA,KACE,aACF,CAOA,GACE,aAAc,CACd,cACF,CAUA,GACE,sBAAuB,CACvB,QAAS,CACT,gBACF,CAOA,IACE,+BAAiC,CACjC,aACF,CASA,EACE,4BACF,CAOA,YACE,kBAAmB,CACnB,yBAA0B,CAC1B,gCACF,CAMA,SAEE,kBACF,CAOA,cAGE,+BAAiC,CACjC,aACF,CAeA,QAEE,aAAc,CACd,aAAc,CACd,iBAAkB,CAClB,uBACF,CAEA,IACE,aACF,CAEA,IACE,SACF,CASA,IACE,iBACF,CAUA,sCAKE,mBAAoB,CACpB,cAAe,CACf,gBAAiB,CACjB,QACF,CAOA,aAEE,gBACF,CAOA,cAEE,mBACF,CAMA,gDAIE,yBACF,CAMA,wHAIE,iBAAkB,CAClB,SACF,CAMA,4GAIE,6BACF,CAMA,SACE,0BACF,CASA,OACE,qBAAsB,CACtB,aAAc,CACd,aAAc,CACd,cAAe,CACf,SAAU,CACV,kBACF,CAMA,SACE,uBACF,CAMA,SACE,aACF,CAOA,6BAEE,qBAAsB,CACtB,SACF,CAMA,kFAEE,WACF,CAOA,cACE,4BAA6B,CAC7B,mBACF,CAMA,yCACE,uBACF,CAOA,6BACE,yBAA0B,CAC1B,YACF,CASA,QACE,aACF,CAMA,QACE,iBACF,CAiBA,kBACE,YACF,CCvVA,aAcE,kEACE,uBAOF,WACE,iDAMF,gCACE,wBAEF,qCAEE,uBADA,uBACA,CAEF,SACE,wBAtBA,CCpBJ,iBAOE,6BAEA,mBANA,qBAEA,sBACA,0BAFA,oBAHA,4BAOA,6BANA,mBAOA,CAEF,gBACE,aCPF,KCGE,mHAEA,wGAGA,wBAAyB,CACzB,wBAAyB,CACzB,4BAA6B,CAC7B,yBAA0B,CAC1B,2BAA4B,CAG5B,sDAAuD,CACvD,gDAAiD,CACjD,wDAAyD,CAGzD,0CAA2C,CAC3C,gDAAiD,CACjD,gDAAiD,CAKjD,gCAAiC,CACjC,sCAAuC,CAGvC,2CAA4C,CAG5C,uCAAwC,CChCxC,+FAGA,uBAAwB,CAGxB,iCAAkC,CAClC,kCAAmC,CAEnC,+BAAgC,CAChC,sCAAuC,CACvC,sCAAuC,CACvC,qGAIA,mDAAoD,CAEpD,mCAAoC,CACpC,8CAA+C,CAC/C,gDAAiD,CACjD,kCAAmC,CACnC,6DAA8D,CAG9D,6BAA8B,CAC9B,6BAA8B,CAC9B,+BAAgC,CAChC,kCAAmC,CACnC,kCAAmC,CCPjC,ukBCYA,srCAZF,kaCVA,mLAOA,oTAWA,2UAaA,0CACA,gEACA,0CAGA,gEAUA,yCACA,+DAGA,4CACA,CACA,iEAGA,sGACA,uCACA,4DAGA,sCACA,2DAEA,4CACA,kEACA,oGACA,CAEA,0GACA,+CAGA,+MAOA,+EACA,wCAIA,4DACA,sEACA,kEACA,sEACA,gDAGA,+DACA,0CACA,gEACA,gGACA,CAGA,2DACA,qDAGA,0CACA,8CACA,oDACA,oDL7GF,iCAEA,iEAME,oCKyGA,yDAIA,sCACA,kCACA,sDAGA,0CACA,kEACA,oDAEA,sDAGA,oCACA,oEAIA,CAGA,yDAGA,qDACA,oDAGA,6DAIA,iEAGA,2DAEA,2DL9IE,4DAEA,gEAIF,gEKgGA,gFAIA,oNAOA,qDAEA,gFAIA,4DAIA,oEAMA,yEAIA,6DACA,0DAGA,uDAGA,qDAEA,wDLpII,6DAEA,yDACE,2DAMN,uCAIA,yCACE,8CAGF,sDMjDA,6DAKA,oCAIA,4CACA,kBAGF,sBAMA,2BAME,qCAGA,qCAEA,iCAEA,+BAEA,mCAEA,qCAIA,CACA,gCACA,gDAKA,kCAIA,6BAEA,0CAQA,kCAIF,8BAGE,8BACA,uCAGF,sCAKE,kCAEA,sDAGA,iCACE,CACA,2FAGA,gCACE,CACA,+DCzEJ,wCAEA,sBAEF,yDAEE,mCACA,wDAGA,2GAGA,wIACE,gDAMJ,kCAGE,6BACA,0CAGA,gEACA,8BACA,uCAKA,sCAIA,kCACA,sDACA,iCACA,sCAOA,sDAKE,gGAIE,+CAGN,sBAEE,yCAMA,0BAOA,yLAKA,aACA,MAEF,6BACE,mBAEA,wCAEF,wCAIE,kCAGA,SACA,kCAKA,mBAGA,CAJA,eACA,CAHF,gBAEE,CAWA,mBACA,mBACA,mDAIA,YACA,mBACA,CAEE,kBAMF,OAPE,kBAOF,oCACA,yCAEA,wBAEA,cADA,WACA,GACA,oBACA,CAFA,gBAEA,aAGF,+CAEE,UAJE,wBAEJ,CAFI,SAIF,CACA,2BACA,GAGA,uBACE,CAJF,yBAGA,CACE,iDACA,uCAEA,yDACE,cACA,wDAKN,yDAIE,uBAEF,kBACE,uBAEA,kDAKA,0DAEA,CAHA,oBAIA,0GAWA,aAEA,CAHA,YAGA,4HAKF,+CAGE,sBAEF,WAKE,0CAGA,CANA,qCAGA,CAJA,WAOA,SAIA,0CACE,CALF,qCAIA,CACE,wBAEA,mBAEJ,gBACE,gBAIA,+CAKF,CAIE,kDAEA,CANF,8BAIE,CAEA,YAGA,CAfF,2BACE,CAHA,UAEF,CAYE,UAGA,2CACF,iEAOE,iCACA,8BAGA,wCAIA,wBAMI,0CAKF,CATA,6DAGA,CALF,qBAEE,CASA,YACA,yBAGA,CAEE,cAKN,CAPI,sBAOJ,gCAGE,qBAEA,WACA,aACA,sCAEA,mBACA,6BAGA,uEADA,qBACA,6BAIA,yBACA,qCAEE,UAEA,YACA,sBAEF,8BAGA,CAPE,aACA,WAMF,4BACE,sBACA,WAMJ,uBACE,cAYE,mBAXA,qDAKA,qCAGA,CAEA,YACA,CAHA,2BAEA,CACA,oCAEA,4CACA,uBAIA,sBAEJ,eAFI,cAIF,iBACE,CAHJ,kBAGI,yBAEA,oCAIA,qDAMF,mEAGE,+CAKA,gCAEA,qCAGA,oCAGE,sBACA,CAJF,WAEE,CAFF,eAEE,SAEA,mBACA,qCACE,aACA,CAFF,YADA,qBACA,WAEE,sBACA,kEAEN,cAEE,CAFF,YAEE,iDAKA,uCAIA,2DAKA,kBAEA,CAHA,sBAGA,mBACA,0BAEJ,yBAII,aADA,WACA,CAMF,UAFE,kBAEF,CAJF,gBAEI,CAFJ,iBAIE,6CC9ZF,yBACE,WACA,iBAEA,aAFA,iBAEA,6BAEA,kCACA,mBAKA,gCAGA,CARA,QAEA,CAGA,UALA,qBAEA,qDAGA,CALA,OAQA,4BACE,cAGF,2BACE,gCAEJ,CAHE,UAGF,8CAGE,CAHF,UAGE,wCAGA,qBACA,CAFA,UAEA,6CAGA,yCAIA,sBAHA,UAGA,kCACE,OACA,CADA,KACA,cAQF,0CACE,CAFF,kBACA,CACE,wEACA,CARA,YACA,CAKF,mBAFF,MACE,CAIE,gBAJF,iCAJE,cAGJ,CANI,oBAEA,CAKF,SAIE,2BADA,UACA,kBAGF,sCACA,CAFF,WACE,WACA,mBACE,kDACA,0EACA,uDAKJ,aACE,mDAII,CAJJ,6CAII,4BACA,sCACE,kEACA,+CACE,aACA,WADA,+BACA,uEANN,YACE,mDAEE,mBADF,0CACE,CADF,qBACE,0DACA,YACE,4DACA,sEANN,YACE,8CACA,kBADA,UACA,2CACE,2EACA,cACE,kEACA,mEANN,yBACE,4DACA,sBACE,+EAEE,iEACA,qEANN,sCACE,CAGE,iBAHF,gBAGE,qBACE,CAJJ,uBACA,gDACE,wDACA,6DAHF,2CACA,CADA,gBACA,eACE,CAGE,sBANN,8BACE,CAII,iBAFF,4DACA,WACE,YADF,uCACE,6EACA,2BANN,8CACE,kDACA,0CACE,8BACA,yFACE,sBACA,sFALJ,mEACA,sBACE,kEACA,6EACE,uCACA,kEALJ,qGAEE,kEACA,6EACE,uCACA,kEALJ,8CACA,uDACE,sEACA,2EACE,sCACA,iEALJ,mGACA,qCACE,oDACA,0DACE,6GACA,gDAGR,yDCrEA,sEACE,CACA,6GACE,gEACF,iGAIF,wFACE,qDAGA,mGAEE,2CAEF,4FACE,gCACF,wGACE,8DAEE,6FAIA,iJAKN,6GACE,gDAKF,yDACA,qCAGA,6BACA,kBACA,qDAKA,oCAEA,+DAGA,2CAGE,oDAIA,oEAEE,qBAGJ,wDAEE,uCAEF,kEAGA,8CAEA,uDAKA,oCAEA,yDAEE,gEAKF,+CC5FA,0EAGE,CACA,qDCLJ,+DAIE,sCAIA,kEACE,yBACA,2FAMA,gBACA,yGCbF,mBAOA,2MAIA,4HAYA,0DACE,8GAYF,8HAQE,mBAEA,6HAOF,YAGA,mIAME,eACA,CAFF,YAEE,4FAMJ,8BAEE,uBAYA,sCAEE,CAJF,oBAEA,CARA,wCAEA,CAHA,8BACA,CAFA,eACA,CAGA,wCAEA,CAEA,mDAIE,kCACE,6BACA,4CAKJ,kDAIA,eACE,aAGF,8BACE,uDACA,sCACA,cAEA,+BACA,CAFA,eAEA,wCAEF,YACE,iBACA,mCACA,0DAGF,qBAEE,CAFF,kBAEE,+BAIA,yCAEE,qBADA,gBACA,yBAKF,eACA,CAFF,YACE,CACA,iBACA,qDAEA,mDCvIJ,2FAOE,iCACA,CAEA,eACA,CAHA,kBAEA,CAFA,wBAGA,8BACA,eACE,CAFF,YAEE,0BACA,8CAGA,oBACE,oCAGA,kBACE,8DAEA,iBAEN,UACE,8BAIJ,+CAEE,qDAEF,kDAIE,YAEF,CAFE,YAEF,CCjCE,mFAJA,QACA,UAIE,CADF,iBACE,mCAGA,iDACE,+BAGF,wBAEA,mBAKA,6CAEF,CAHE,mBACA,CAEF,kCAIE,CARA,kBACA,CAFF,eASE,YACA,mBAGF,CAJE,UAIF,wCCjCA,oBDmCE,wBCpCJ,uCACE,8BACA,4CACA,oBAGA,2CCAA,6CAGE,CAPF,uBAIA,CDGA,gDACE,6BCVJ,CAWM,2CAEF,CAJA,kCAEE,CDJF,aCLF,gBDKE,uBCMA,gCAGA,gDAGE,wBAGJ,0BAEA,iBACE,aACF,CADE,UACF,uBACE,aACF,oBACE,YACF,4BACE,6CAMA,CAYF,6DAZE,mCAGE,iCASJ,4BAGE,4DADA,+BACA,CAFA,qBAEA,yBACE,aAEF,wBAHA,SAGA,iHACE,2DAKF,CANA,yCACE,CADF,oCAMA,uSAIA,sGACE,oDChEJ,WAEF,yBACE,QACA,eAEA,gBAEE,uCAGA,CALF,iCAKE,uCAGA,0BACA,CACA,oBACA,iCClBJ,gBACE,KAGF,qBACE,YAGF,CAHE,cAGF,gCAEE,mBACA,iEAEA,oCACA,wCAEA,sBACA,WAEA,CAFA,YAEA,8EAEA,mCAFA,iBAEA,6BAIA,wEAKA,sDAIE,CARF,mDAIA,CAIE,cAEF,8CAIA,oBAFE,iBAEF,8CAGE,eAEF,CAFE,YAEF,OAEE,kBAGJ,CAJI,eACA,CAFF,mBAKF,yCCjDE,oBACA,CAFA,iBAEA,uCAKE,iBACA,qCAGA,mBCZJ,CDWI,gBCXJ,6BAEE,eACA,sBAGA,eAEA,sBACA,oDACA,iGAMA,gBAFE,YAEF,8FAME,iJClBF,YACA,gNAUE,6BAEF,oTAcI,kBACF,gHAIA,qBACE,eACF,qDACE,kBACF,6DACE,4BCxCJ,oBAEF,qCAEI,+CAGF,uBACE,uDAGJ,oBAkBE,mDAhBA,+CAaA,CAbA,oBAaA,0FAEE,CAFF,gGAbA,+BAaA,0BAGA,mQAIA,oNAEE,iBAGJ,CAHI,gBADA,gBAIJ,8CAYI,CAZJ,wCAYI,sVACE,iCAGA,uEAHA,QAGA,qXAKJ,iDAGF,CARM,+CACE,iDAIN,CALI,gBAQN,mHACE,gBAGF,2DACE,0EAOA,0EAKA,6EC/EA,iDACA,gCACA,oDAGA,qBACA,oDCFA,cACA,eAEA,yBAGF,sBAEE,iBACA,sNAWA,iBACE,kBACA,wRAgBA,kBAEA,iOAgBA,uCACE,uEAEA,kBAEF,qUAuBE,iDAIJ,CACA,geCxFF,4BAEE,CAQA,6JACA,iDAIA,sEAGA,mDAOF,iDAGE,4DAIA,8CACA,qDAEE,eAFF,cAEE,oBAEF,uBAFE,kCAGA,eACA,iBACA,mBAIA,mDACA,CAHA,uCAEA,CAJA,0CACA,CAIA,gBAJA,gBACA,oBADA,gBAIA,wBAEJ,gBAGE,6BACA,YAHA,iBAGA,gCACA,iEAEA,6CACA,sDACA,0BADA,wBACA,0BACA,oIAIA,mBAFA,YAEA,qBACA,0CAIE,uBAEF,CAHA,yBACE,CAEF,iDACE,mFAKJ,oCACE,CANE,aAKJ,CACE,qEAIA,YAFA,WAEA,CAHA,aACA,CAEA,gBACE,4BACA,sBADA,aACA,gCAMF,oCACA,yDACA,2CAEA,qBAGE,kBAEA,CACA,mCAIF,CARE,YACA,CAOF,iCAEE,CAPA,oBACA,CAQA,oBACE,uDAEJ,sDAGA,CAHA,cAGA,0BACE,oDAIA,oCACA,4BACA,sBAGA,cAEA,oFAGA,sBAEA,yDACE,CAIA,iBAJA,wBAIA,6CAJA,6CAOA,4BAGJ,CAHI,cAGJ,yCAGA,kBACE,CAIA,iDAEA,CATA,YAEF,CACE,4CAGA,kBAIA,wEAEA,wDAIF,kCAOE,iDACA,CARF,WAIE,sCAGA,CANA,2CACA,CAMA,oEARF,iBACE,CACA,qCAMA,iBAuBE,uBAlBF,YAKA,2DALA,uDAKA,CALA,sBAiBA,4CACE,CALA,gRAIF,YACE,UAEN,uBACE,YACA,mCAOE,+CAGA,8BAGF,+CAGA,4BCjNA,SDiNA,qFCjNA,gDAGA,sCACA,qCACA,sDAIF,CAIE,kDAGA,CAPF,0CAOE,kBAEA,kDAEA,CAHA,eACA,CAFA,YACA,CADA,SAIA,mHAIE,CAGA,6CAFA,oCAeE,CAbF,yBACE,qBAEJ,CAGE,oBACA,CAEA,YAFA,2CACF,CACE,uBAEA,mFAEE,CALJ,oBACE,CAEA,UAEE,gCAGF,sDAEA,yCC7CJ,oCAGA,CD6CE,yXAQE,sCCrDJ,wCAGA,oCACE","sources":["webpack:///./node_modules/normalize.css/normalize.css","webpack:///./src/furo/assets/styles/base/_print.sass","webpack:///./src/furo/assets/styles/base/_screen-readers.sass","webpack:///./src/furo/assets/styles/base/_theme.sass","webpack:///./src/furo/assets/styles/variables/_fonts.scss","webpack:///./src/furo/assets/styles/variables/_spacing.scss","webpack:///./src/furo/assets/styles/variables/_icons.scss","webpack:///./src/furo/assets/styles/variables/_admonitions.scss","webpack:///./src/furo/assets/styles/variables/_colors.scss","webpack:///./src/furo/assets/styles/base/_typography.sass","webpack:///./src/furo/assets/styles/_scaffold.sass","webpack:///./src/furo/assets/styles/content/_admonitions.sass","webpack:///./src/furo/assets/styles/content/_api.sass","webpack:///./src/furo/assets/styles/content/_blocks.sass","webpack:///./src/furo/assets/styles/content/_captions.sass","webpack:///./src/furo/assets/styles/content/_code.sass","webpack:///./src/furo/assets/styles/content/_footnotes.sass","webpack:///./src/furo/assets/styles/content/_images.sass","webpack:///./src/furo/assets/styles/content/_indexes.sass","webpack:///./src/furo/assets/styles/content/_lists.sass","webpack:///./src/furo/assets/styles/content/_math.sass","webpack:///./src/furo/assets/styles/content/_misc.sass","webpack:///./src/furo/assets/styles/content/_rubrics.sass","webpack:///./src/furo/assets/styles/content/_sidebar.sass","webpack:///./src/furo/assets/styles/content/_tables.sass","webpack:///./src/furo/assets/styles/content/_target.sass","webpack:///./src/furo/assets/styles/content/_gui-labels.sass","webpack:///./src/furo/assets/styles/components/_footer.sass","webpack:///./src/furo/assets/styles/components/_sidebar.sass","webpack:///./src/furo/assets/styles/components/_table_of_contents.sass","webpack:///./src/furo/assets/styles/_shame.sass"],"sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n","// This file contains styles for managing print media.\n\n////////////////////////////////////////////////////////////////////////////////\n// Hide elements not relevant to print media.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Hide icon container.\n .content-icon-container\n display: none !important\n\n // Hide showing header links if hovering over when printing.\n .headerlink\n display: none !important\n\n // Hide mobile header.\n .mobile-header\n display: none !important\n\n // Hide navigation links.\n .related-pages\n display: none !important\n\n////////////////////////////////////////////////////////////////////////////////\n// Tweaks related to decolorization.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Apply a border around code which no longer have a color background.\n .highlight\n border: 0.1pt solid var(--color-foreground-border)\n\n////////////////////////////////////////////////////////////////////////////////\n// Avoid page break in some relevant cases.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n ul, ol, dl, a, table, pre, blockquote\n page-break-inside: avoid\n\n h1, h2, h3, h4, h5, h6, img, figure, caption\n page-break-inside: avoid\n page-break-after: avoid\n\n ul, ol, dl\n page-break-before: avoid\n",".visually-hidden\n position: absolute !important\n width: 1px !important\n height: 1px !important\n padding: 0 !important\n margin: -1px !important\n overflow: hidden !important\n clip: rect(0,0,0,0) !important\n white-space: nowrap !important\n border: 0 !important\n\n:-moz-focusring\n outline: auto\n","// This file serves as the \"skeleton\" of the theming logic.\n//\n// This contains the bulk of the logic for handling dark mode, color scheme\n// toggling and the handling of color-scheme-specific hiding of elements.\n\nbody\n @include fonts\n @include spacing\n @include icons\n @include admonitions\n @include default-admonition(#651fff, \"abstract\")\n @include default-topic(#14B8A6, \"pencil\")\n\n @include colors\n\n.only-light\n display: block !important\nhtml body .only-dark\n display: none !important\n\n// Ignore dark-mode hints if print media.\n@media not print\n // Enable dark-mode, if requested.\n body[data-theme=\"dark\"]\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n // Enable dark mode, unless explicitly told to avoid.\n @media (prefers-color-scheme: dark)\n body:not([data-theme=\"light\"])\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n//\n// Theme toggle presentation\n//\nbody[data-theme=\"auto\"]\n .theme-toggle svg.theme-icon-when-auto\n display: block\n\nbody[data-theme=\"dark\"]\n .theme-toggle svg.theme-icon-when-dark\n display: block\n\nbody[data-theme=\"light\"]\n .theme-toggle svg.theme-icon-when-light\n display: block\n","// Fonts used by this theme.\n//\n// There are basically two things here -- using the system font stack and\n// defining sizes for various elements in %ages. We could have also used `em`\n// but %age is easier to reason about for me.\n\n@mixin fonts {\n // These are adapted from https://systemfontstack.com/\n --font-stack: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji;\n --font-stack--monospace: \"SFMono-Regular\", Menlo, Consolas, Monaco,\n Liberation Mono, Lucida Console, monospace;\n\n --font-size--normal: 100%;\n --font-size--small: 87.5%;\n --font-size--small--2: 81.25%;\n --font-size--small--3: 75%;\n --font-size--small--4: 62.5%;\n\n // Sidebar\n --sidebar-caption-font-size: var(--font-size--small--2);\n --sidebar-item-font-size: var(--font-size--small);\n --sidebar-search-input-font-size: var(--font-size--small);\n\n // Table of Contents\n --toc-font-size: var(--font-size--small--3);\n --toc-font-size--mobile: var(--font-size--normal);\n --toc-title-font-size: var(--font-size--small--4);\n\n // Admonitions\n //\n // These aren't defined in terms of %ages, since nesting these is permitted.\n --admonition-font-size: 0.8125rem;\n --admonition-title-font-size: 0.8125rem;\n\n // Code\n --code-font-size: var(--font-size--small--2);\n\n // API\n --api-font-size: var(--font-size--small);\n}\n","// Spacing for various elements on the page\n//\n// If the user wants to tweak things in a certain way, they are permitted to.\n// They also have to deal with the consequences though!\n\n@mixin spacing {\n // Header!\n --header-height: calc(\n var(--sidebar-item-line-height) + 4 * #{var(--sidebar-item-spacing-vertical)}\n );\n --header-padding: 0.5rem;\n\n // Sidebar\n --sidebar-tree-space-above: 1.5rem;\n --sidebar-caption-space-above: 1rem;\n\n --sidebar-item-line-height: 1rem;\n --sidebar-item-spacing-vertical: 0.5rem;\n --sidebar-item-spacing-horizontal: 1rem;\n --sidebar-item-height: calc(\n var(--sidebar-item-line-height) + 2 *#{var(--sidebar-item-spacing-vertical)}\n );\n\n --sidebar-expander-width: var(--sidebar-item-height); // be square\n\n --sidebar-search-space-above: 0.5rem;\n --sidebar-search-input-spacing-vertical: 0.5rem;\n --sidebar-search-input-spacing-horizontal: 0.5rem;\n --sidebar-search-input-height: 1rem;\n --sidebar-search-icon-size: var(--sidebar-search-input-height);\n\n // Table of Contents\n --toc-title-padding: 0.25rem 0;\n --toc-spacing-vertical: 1.5rem;\n --toc-spacing-horizontal: 1.5rem;\n --toc-item-spacing-vertical: 0.4rem;\n --toc-item-spacing-horizontal: 1rem;\n}\n","// Expose theme icons as CSS variables.\n\n$icons: (\n // Adapted from tabler-icons\n // url: https://tablericons.com/\n \"search\":\n url('data:image/svg+xml;charset=utf-8,'),\n // Factored out from mkdocs-material on 24-Aug-2020.\n // url: https://squidfunk.github.io/mkdocs-material/reference/admonitions/\n \"pencil\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"abstract\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"info\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"flame\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"question\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"warning\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"failure\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"spark\":\n url('data:image/svg+xml;charset=utf-8,')\n);\n\n@mixin icons {\n @each $name, $glyph in $icons {\n --icon-#{$name}: #{$glyph};\n }\n}\n","// Admonitions\n\n// Structure of these is:\n// admonition-class: color \"icon-name\";\n//\n// The colors are translated into CSS variables below. The icons are\n// used directly in the main declarations to set the `mask-image` in\n// the title.\n\n// prettier-ignore\n$admonitions: (\n // Each of these has an reST directives for it.\n \"caution\": #ff9100 \"spark\",\n \"warning\": #ff9100 \"warning\",\n \"danger\": #ff5252 \"spark\",\n \"attention\": #ff5252 \"warning\",\n \"error\": #ff5252 \"failure\",\n \"hint\": #00c852 \"question\",\n \"tip\": #00c852 \"info\",\n \"important\": #00bfa5 \"flame\",\n \"note\": #00b0ff \"pencil\",\n \"seealso\": #448aff \"info\",\n \"admonition-todo\": #808080 \"pencil\"\n);\n\n@mixin default-admonition($color, $icon-name) {\n --color-admonition-title: #{$color};\n --color-admonition-title-background: #{rgba($color, 0.2)};\n\n --icon-admonition-default: var(--icon-#{$icon-name});\n}\n\n@mixin default-topic($color, $icon-name) {\n --color-topic-title: #{$color};\n --color-topic-title-background: #{rgba($color, 0.2)};\n\n --icon-topic-default: var(--icon-#{$icon-name});\n}\n\n@mixin admonitions {\n @each $name, $values in $admonitions {\n --color-admonition-title--#{$name}: #{nth($values, 1)};\n --color-admonition-title-background--#{$name}: #{rgba(\n nth($values, 1),\n 0.2\n )};\n }\n}\n","// Colors used throughout this theme.\n//\n// The aim is to give the user more control. Thus, instead of hard-coding colors\n// in various parts of the stylesheet, the approach taken is to define all\n// colors as CSS variables and reusing them in all the places.\n//\n// `colors-dark` depends on `colors` being included at a lower specificity.\n\n@mixin colors {\n --color-problematic: #b30000;\n\n // Base Colors\n --color-foreground-primary: black; // for main text and headings\n --color-foreground-secondary: #5a5c63; // for secondary text\n --color-foreground-muted: #646776; // for muted text\n --color-foreground-border: #878787; // for content borders\n\n --color-background-primary: white; // for content\n --color-background-secondary: #f8f9fb; // for navigation + ToC\n --color-background-hover: #efeff4ff; // for navigation-item hover\n --color-background-hover--transparent: #efeff400;\n --color-background-border: #eeebee; // for UI borders\n --color-background-item: #ccc; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2962ff;\n --color-brand-content: #2a5adf;\n\n // API documentation\n --color-api-background: var(--color-background-hover--transparent);\n --color-api-background-hover: var(--color-background-hover);\n --color-api-overall: var(--color-foreground-secondary);\n --color-api-name: var(--color-problematic);\n --color-api-pre-name: var(--color-problematic);\n --color-api-paren: var(--color-foreground-secondary);\n --color-api-keyword: var(--color-foreground-primary);\n --color-highlight-on-target: #ffffcc;\n\n // Inline code background\n --color-inline-code-background: var(--color-background-secondary);\n\n // Highlighted text (search)\n --color-highlighted-background: #ddeeff;\n --color-highlighted-text: var(--color-foreground-primary);\n\n // GUI Labels\n --color-guilabel-background: #ddeeff80;\n --color-guilabel-border: #bedaf580;\n --color-guilabel-text: var(--color-foreground-primary);\n\n // Admonitions!\n --color-admonition-background: transparent;\n\n //////////////////////////////////////////////////////////////////////////////\n // Everything below this should be one of:\n // - var(...)\n // - *-gradient(...)\n // - special literal values (eg: transparent, none)\n //////////////////////////////////////////////////////////////////////////////\n\n // Tables\n --color-table-header-background: var(--color-background-secondary);\n --color-table-border: var(--color-background-border);\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: transparent;\n --color-card-marginals-background: var(--color-background-secondary);\n\n // Header\n --color-header-background: var(--color-background-primary);\n --color-header-border: var(--color-background-border);\n --color-header-text: var(--color-foreground-primary);\n\n // Sidebar (left)\n --color-sidebar-background: var(--color-background-secondary);\n --color-sidebar-background-border: var(--color-background-border);\n\n --color-sidebar-brand-text: var(--color-foreground-primary);\n --color-sidebar-caption-text: var(--color-foreground-muted);\n --color-sidebar-link-text: var(--color-foreground-secondary);\n --color-sidebar-link-text--top-level: var(--color-brand-primary);\n\n --color-sidebar-item-background: var(--color-sidebar-background);\n --color-sidebar-item-background--current: var(\n --color-sidebar-item-background\n );\n --color-sidebar-item-background--hover: linear-gradient(\n 90deg,\n var(--color-background-hover--transparent) 0%,\n var(--color-background-hover) var(--sidebar-item-spacing-horizontal),\n var(--color-background-hover) 100%\n );\n\n --color-sidebar-item-expander-background: transparent;\n --color-sidebar-item-expander-background--hover: var(\n --color-background-hover\n );\n\n --color-sidebar-search-text: var(--color-foreground-primary);\n --color-sidebar-search-background: var(--color-background-secondary);\n --color-sidebar-search-background--focus: var(--color-background-primary);\n --color-sidebar-search-border: var(--color-background-border);\n --color-sidebar-search-icon: var(--color-foreground-muted);\n\n // Table of Contents (right)\n --color-toc-background: var(--color-background-primary);\n --color-toc-title-text: var(--color-foreground-muted);\n --color-toc-item-text: var(--color-foreground-secondary);\n --color-toc-item-text--hover: var(--color-foreground-primary);\n --color-toc-item-text--active: var(--color-brand-primary);\n\n // Actual page contents\n --color-content-foreground: var(--color-foreground-primary);\n --color-content-background: transparent;\n\n // Links\n --color-link: var(--color-brand-content);\n --color-link--hover: var(--color-brand-content);\n --color-link-underline: var(--color-background-border);\n --color-link-underline--hover: var(--color-foreground-border);\n}\n\n@mixin colors-dark {\n --color-problematic: #ee5151;\n\n // Base Colors\n --color-foreground-primary: #ffffffcc; // for main text and headings\n --color-foreground-secondary: #9ca0a5; // for secondary text\n --color-foreground-muted: #81868d; // for muted text\n --color-foreground-border: #666666; // for content borders\n\n --color-background-primary: #131416; // for content\n --color-background-secondary: #1a1c1e; // for navigation + ToC\n --color-background-hover: #1e2124ff; // for navigation-item hover\n --color-background-hover--transparent: #1e212400;\n --color-background-border: #303335; // for UI borders\n --color-background-item: #444; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2b8cee;\n --color-brand-content: #368ce2;\n\n // Highlighted text (search)\n --color-highlighted-background: #083563;\n\n // GUI Labels\n --color-guilabel-background: #08356380;\n --color-guilabel-border: #13395f80;\n\n // API documentation\n --color-api-keyword: var(--color-foreground-secondary);\n --color-highlight-on-target: #333300;\n\n // Admonitions\n --color-admonition-background: #18181a;\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: #18181a;\n --color-card-marginals-background: var(--color-background-hover);\n}\n","// This file contains the styling for making the content throughout the page,\n// including fonts, paragraphs, headings and spacing among these elements.\n\nbody\n font-family: var(--font-stack)\npre,\ncode,\nkbd,\nsamp\n font-family: var(--font-stack--monospace)\n\n// Make fonts look slightly nicer.\nbody\n -webkit-font-smoothing: antialiased\n -moz-osx-font-smoothing: grayscale\n\n// Line height from Bootstrap 4.1\narticle\n line-height: 1.5\n\n//\n// Headings\n//\nh1,\nh2,\nh3,\nh4,\nh5,\nh6\n line-height: 1.25\n font-weight: bold\n\n border-radius: 0.5rem\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n margin-left: -0.5rem\n margin-right: -0.5rem\n padding-left: 0.5rem\n padding-right: 0.5rem\n\n + p\n margin-top: 0\n\nh1\n font-size: 2.5em\n margin-top: 1.75rem\n margin-bottom: 1rem\nh2\n font-size: 2em\n margin-top: 1.75rem\nh3\n font-size: 1.5em\nh4\n font-size: 1.25em\nh5\n font-size: 1.125em\nh6\n font-size: 1em\n\nsmall\n opacity: 75%\n font-size: 80%\n\n// Paragraph\np\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n\n// Horizontal rules\nhr.docutils\n height: 1px\n padding: 0\n margin: 2rem 0\n background-color: var(--color-background-border)\n border: 0\n\n.centered\n text-align: center\n\n// Links\na\n text-decoration: underline\n\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &.muted-link\n color: inherit\n &:hover\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline--hover)\n","// This file contains the styles for the overall layouting of the documentation\n// skeleton, including the responsive changes as well as sidebar toggles.\n//\n// This is implemented as a mobile-last design, which isn't ideal, but it is\n// reasonably good-enough and I got pretty tired by the time I'd finished this\n// to move the rules around to fix this. Shouldn't take more than 3-4 hours,\n// if you know what you're doing tho.\n\n// HACK: Not all browsers account for the scrollbar width in media queries.\n// This results in horizontal scrollbars in the breakpoint where we go\n// from displaying everything to hiding the ToC. We accomodate for this by\n// adding a bit of padding to the TOC drawer, disabling the horizontal\n// scrollbar and allowing the scrollbars to cover the padding.\n// https://www.456bereastreet.com/archive/201301/media_query_width_and_vertical_scrollbars/\n\n// HACK: Always having the scrollbar visible, prevents certain browsers from\n// causing the content to stutter horizontally between taller-than-viewport and\n// not-taller-than-viewport pages.\n\nhtml\n overflow-x: hidden\n overflow-y: scroll\n scroll-behavior: smooth\n\n.sidebar-scroll, .toc-scroll, article[role=main] *\n // Override Firefox scrollbar style\n scrollbar-width: thin\n scrollbar-color: var(--color-foreground-border) transparent\n\n // Override Chrome scrollbar styles\n &::-webkit-scrollbar\n width: 0.25rem\n height: 0.25rem\n &::-webkit-scrollbar-thumb\n background-color: var(--color-foreground-border)\n border-radius: 0.125rem\n\n//\n// Overalls\n//\nhtml,\nbody\n height: 100%\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\narticle\n color: var(--color-content-foreground)\n background: var(--color-content-background)\n overflow-wrap: break-word\n\n.page\n display: flex\n // fill the viewport for pages with little content.\n min-height: 100%\n\n.mobile-header\n width: 100%\n height: var(--header-height)\n background-color: var(--color-header-background)\n color: var(--color-header-text)\n border-bottom: 1px solid var(--color-header-border)\n\n // Looks like sub-script/super-script have this, and we need this to\n // be \"on top\" of those.\n z-index: 10\n\n // We don't show the header on large screens.\n display: none\n\n // Add shadow when scrolled\n &.scrolled\n border-bottom: none\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2)\n\n .header-center\n a\n color: var(--color-header-text)\n text-decoration: none\n\n.main\n display: flex\n flex: 1\n\n// Sidebar (left) also covers the entire left portion of screen.\n.sidebar-drawer\n box-sizing: border-box\n\n border-right: 1px solid var(--color-sidebar-background-border)\n background: var(--color-sidebar-background)\n\n display: flex\n justify-content: flex-end\n // These next two lines took me two days to figure out.\n width: calc((100% - #{$full-width}) / 2 + #{$sidebar-width})\n min-width: $sidebar-width\n\n// Scroll-along sidebars\n.sidebar-container,\n.toc-drawer\n box-sizing: border-box\n width: $sidebar-width\n\n.toc-drawer\n background: var(--color-toc-background)\n // See HACK described on top of this document\n padding-right: 1rem\n\n.sidebar-sticky,\n.toc-sticky\n position: sticky\n top: 0\n height: min(100%, 100vh)\n height: 100vh\n\n display: flex\n flex-direction: column\n\n.sidebar-scroll,\n.toc-scroll\n flex-grow: 1\n flex-shrink: 1\n\n overflow: auto\n scroll-behavior: smooth\n\n// Central items.\n.content\n padding: 0 $content-padding\n width: $content-width\n\n display: flex\n flex-direction: column\n justify-content: space-between\n\n.icon\n display: inline-block\n height: 1rem\n width: 1rem\n svg\n width: 100%\n height: 100%\n\n//\n// Accommodate announcement banner\n//\n.announcement\n background-color: var(--color-announcement-background)\n color: var(--color-announcement-text)\n\n height: var(--header-height)\n display: flex\n align-items: center\n overflow-x: auto\n & + .page\n min-height: calc(100% - var(--header-height))\n\n.announcement-content\n box-sizing: border-box\n padding: 0.5rem\n min-width: 100%\n white-space: nowrap\n text-align: center\n\n a\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-announcement-text)\n\n &:hover\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-link--hover)\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for theme\n////////////////////////////////////////////////////////////////////////////////\n.no-js .theme-toggle-container // don't show theme toggle if there's no JS\n display: none\n\n.theme-toggle-container\n vertical-align: middle\n\n.theme-toggle\n cursor: pointer\n border: none\n padding: 0\n background: transparent\n\n.theme-toggle svg\n vertical-align: middle\n height: 1rem\n width: 1rem\n color: var(--color-foreground-primary)\n display: none\n\n.theme-toggle-header\n float: left\n padding: 1rem 0.5rem\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for elements\n////////////////////////////////////////////////////////////////////////////////\n.toc-overlay-icon, .nav-overlay-icon\n display: none\n cursor: pointer\n\n .icon\n color: var(--color-foreground-secondary)\n height: 1rem\n width: 1rem\n\n.toc-header-icon, .nav-overlay-icon\n // for when we set display: flex\n justify-content: center\n align-items: center\n\n.toc-content-icon\n height: 1.5rem\n width: 1.5rem\n\n.content-icon-container\n float: right\n display: flex\n margin-top: 1.5rem\n margin-left: 1rem\n margin-bottom: 1rem\n gap: 0.5rem\n\n .edit-this-page svg\n color: inherit\n height: 1rem\n width: 1rem\n\n.sidebar-toggle\n position: absolute\n display: none\n// \n.sidebar-toggle[name=\"__toc\"]\n left: 20px\n.sidebar-toggle:checked\n left: 40px\n// \n\n.overlay\n position: fixed\n top: 0\n width: 0\n height: 0\n\n transition: width 0ms, height 0ms, opacity 250ms ease-out\n\n opacity: 0\n background-color: rgba(0, 0, 0, 0.54)\n.sidebar-overlay\n z-index: 20\n.toc-overlay\n z-index: 40\n\n// Keep things on top and smooth.\n.sidebar-drawer\n z-index: 30\n transition: left 250ms ease-in-out\n.toc-drawer\n z-index: 50\n transition: right 250ms ease-in-out\n\n// Show the Sidebar\n#__navigation:checked\n & ~ .sidebar-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .sidebar-drawer\n top: 0\n left: 0\n // Show the toc sidebar\n#__toc:checked\n & ~ .toc-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .toc-drawer\n top: 0\n right: 0\n\n////////////////////////////////////////////////////////////////////////////////\n// Back to top\n////////////////////////////////////////////////////////////////////////////////\n.back-to-top\n text-decoration: none\n\n display: none\n position: fixed\n left: 0\n top: 1rem\n padding: 0.5rem\n padding-right: 0.75rem\n border-radius: 1rem\n font-size: 0.8125rem\n\n background: var(--color-background-primary)\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), #6b728080 0px 0px 1px 0px\n\n z-index: 10\n\n margin-left: 50%\n transform: translateX(-50%)\n svg\n height: 1rem\n width: 1rem\n fill: currentColor\n display: inline-block\n\n span\n margin-left: 0.25rem\n\n .show-back-to-top &\n display: flex\n align-items: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Responsive layouting\n////////////////////////////////////////////////////////////////////////////////\n// Make things a bit bigger on bigger screens.\n@media (min-width: $full-width + $sidebar-width)\n html\n font-size: 110%\n\n@media (max-width: $full-width)\n // Collapse \"toc\" into the icon.\n .toc-content-icon\n display: flex\n .toc-drawer\n position: fixed\n height: 100vh\n top: 0\n right: -$sidebar-width\n border-left: 1px solid var(--color-background-muted)\n .toc-tree\n border-left: none\n font-size: var(--toc-font-size--mobile)\n\n // Accomodate for a changed content width.\n .sidebar-drawer\n width: calc((100% - #{$full-width - $sidebar-width}) / 2 + #{$sidebar-width})\n\n@media (max-width: $full-width - $sidebar-width)\n // Collapse \"navigation\".\n .nav-overlay-icon\n display: flex\n .sidebar-drawer\n position: fixed\n height: 100vh\n width: $sidebar-width\n\n top: 0\n left: -$sidebar-width\n\n // Swap which icon is visible.\n .toc-header-icon\n display: flex\n .toc-content-icon, .theme-toggle-content\n display: none\n .theme-toggle-header\n display: block\n\n // Show the header.\n .mobile-header\n position: sticky\n top: 0\n display: flex\n justify-content: space-between\n align-items: center\n\n .header-left,\n .header-right\n display: flex\n height: var(--header-height)\n padding: 0 var(--header-padding)\n label\n height: 100%\n width: 100%\n user-select: none\n\n .nav-overlay-icon .icon,\n .theme-toggle svg\n height: 1.25rem\n width: 1.25rem\n\n // Add a scroll margin for the content\n :target\n scroll-margin-top: var(--header-height)\n\n // Show back-to-top below the header\n .back-to-top\n top: calc(var(--header-height) + 0.5rem)\n\n // Center the page, and accommodate for the header.\n .page\n flex-direction: column\n justify-content: center\n .content\n margin-left: auto\n margin-right: auto\n\n@media (max-width: $content-width + 2* $content-padding)\n // Content should respect window limits.\n .content\n width: 100%\n overflow-x: auto\n\n@media (max-width: $content-width)\n .content\n padding: 0 $content-padding--small\n // Don't float sidebars to the right.\n article aside.sidebar\n float: none\n width: 100%\n margin: 1rem 0\n","//\n// The design here is strongly inspired by mkdocs-material.\n.admonition, .topic\n margin: 1rem auto\n padding: 0 0.5rem 0.5rem 0.5rem\n\n background: var(--color-admonition-background)\n\n border-radius: 0.2rem\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n font-size: var(--admonition-font-size)\n\n overflow: hidden\n page-break-inside: avoid\n\n // First element should have no margin, since the title has it.\n > :nth-child(2)\n margin-top: 0\n\n // Last item should have no margin, since we'll control that w/ padding\n > :last-child\n margin-bottom: 0\n\n.admonition p.admonition-title,\np.topic-title\n position: relative\n margin: 0 -0.5rem 0.5rem\n padding-left: 2rem\n padding-right: .5rem\n padding-top: .4rem\n padding-bottom: .4rem\n\n font-weight: 500\n font-size: var(--admonition-title-font-size)\n line-height: 1.3\n\n // Our fancy icon\n &::before\n content: \"\"\n position: absolute\n left: 0.5rem\n width: 1rem\n height: 1rem\n\n// Default styles\np.admonition-title\n background-color: var(--color-admonition-title-background)\n &::before\n background-color: var(--color-admonition-title)\n mask-image: var(--icon-admonition-default)\n mask-repeat: no-repeat\n\np.topic-title\n background-color: var(--color-topic-title-background)\n &::before\n background-color: var(--color-topic-title)\n mask-image: var(--icon-topic-default)\n mask-repeat: no-repeat\n\n//\n// Variants\n//\n.admonition\n border-left: 0.2rem solid var(--color-admonition-title)\n\n @each $type, $value in $admonitions\n &.#{$type}\n border-left-color: var(--color-admonition-title--#{$type})\n > .admonition-title\n background-color: var(--color-admonition-title-background--#{$type})\n &::before\n background-color: var(--color-admonition-title--#{$type})\n mask-image: var(--icon-#{nth($value, 2)})\n\n.admonition-todo > .admonition-title\n text-transform: uppercase\n","// This file stylizes the API documentation (stuff generated by autodoc). It's\n// deeply nested due to how autodoc structures the HTML without enough classes\n// to select the relevant items.\n\n// API docs!\ndl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)\n // Tweak the spacing of all the things!\n dd\n margin-left: 2rem\n > :first-child\n margin-top: 0.125rem\n > :last-child\n margin-bottom: 0.75rem\n\n // This is used for the arguments\n .field-list\n margin-bottom: 0.75rem\n\n // \"Headings\" (like \"Parameters\" and \"Return\")\n > dt\n text-transform: uppercase\n font-size: var(--font-size--small)\n\n dd:empty\n margin-bottom: 0.5rem\n dd > ul\n margin-left: -1.2rem\n > li\n > p:nth-child(2)\n margin-top: 0\n // When the last-empty-paragraph follows a paragraph, it doesn't need\n // to augument the existing spacing.\n > p + p:last-child:empty\n margin-top: 0\n margin-bottom: 0\n\n // Colorize the elements\n > dt\n color: var(--color-api-overall)\n\n.sig:not(.sig-inline)\n font-weight: bold\n\n font-size: var(--api-font-size)\n font-family: var(--font-stack--monospace)\n\n margin-left: -0.25rem\n margin-right: -0.25rem\n padding-top: 0.25rem\n padding-bottom: 0.25rem\n padding-right: 0.5rem\n\n // These are intentionally em, to properly match the font size.\n padding-left: 3em\n text-indent: -2.5em\n\n border-radius: 0.25rem\n\n background: var(--color-api-background)\n transition: background 100ms ease-out\n\n &:hover\n background: var(--color-api-background-hover)\n\n // adjust the size of the [source] link on the right.\n a.reference\n .viewcode-link\n font-weight: normal\n width: 3.5rem\n\nem.property\n font-style: normal\n &:first-child\n color: var(--color-api-keyword)\n.sig-name\n color: var(--color-api-name)\n.sig-prename\n font-weight: normal\n color: var(--color-api-pre-name)\n.sig-paren\n color: var(--color-api-paren)\n.sig-param\n font-style: normal\n\n.versionmodified\n font-style: italic\ndiv.versionadded, div.versionchanged, div.deprecated\n p\n margin-top: 0.125rem\n margin-bottom: 0.125rem\n\n// Align the [docs] and [source] to the right.\n.viewcode-link, .viewcode-back\n float: right\n text-align: right\n",".line-block\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n .line-block\n margin-top: 0rem\n margin-bottom: 0rem\n padding-left: 1rem\n","// Captions\narticle p.caption,\ntable > caption,\n.code-block-caption\n font-size: var(--font-size--small)\n text-align: center\n\n// Caption above a TOCTree\n.toctree-wrapper.compound\n .caption, :not(.caption) > .caption-text\n font-size: var(--font-size--small)\n text-transform: uppercase\n\n text-align: initial\n margin-bottom: 0\n\n > ul\n margin-top: 0\n margin-bottom: 0\n","// Inline code\ncode.literal, .sig-inline\n background: var(--color-inline-code-background)\n border-radius: 0.2em\n // Make the font smaller, and use padding to recover.\n font-size: var(--font-size--small--2)\n padding: 0.1em 0.2em\n\n pre.literal-block &\n font-size: inherit\n padding: 0\n\n p &\n border: 1px solid var(--color-background-border)\n\n.sig-inline\n font-family: var(--font-stack--monospace)\n\n// Code and Literal Blocks\n$code-spacing-vertical: 0.625rem\n$code-spacing-horizontal: 0.875rem\n\n// Wraps every literal block + line numbers.\ndiv[class*=\" highlight-\"],\ndiv[class^=\"highlight-\"]\n margin: 1em 0\n display: flex\n\n .table-wrapper\n margin: 0\n padding: 0\n\npre\n margin: 0\n padding: 0\n overflow: auto\n\n // Needed to have more specificity than pygments' \"pre\" selector. :(\n article[role=\"main\"] .highlight &\n line-height: 1.5\n\n &.literal-block,\n .highlight &\n font-size: var(--code-font-size)\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n // Make it look like all the other blocks.\n &.literal-block\n margin-top: 1rem\n margin-bottom: 1rem\n\n border-radius: 0.2rem\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n\n// All code is always contained in this.\n.highlight\n width: 100%\n border-radius: 0.2rem\n\n // Make line numbers and prompts un-selectable.\n .gp, span.linenos\n user-select: none\n pointer-events: none\n\n // Expand the line-highlighting.\n .hll\n display: block\n margin-left: -$code-spacing-horizontal\n margin-right: -$code-spacing-horizontal\n padding-left: $code-spacing-horizontal\n padding-right: $code-spacing-horizontal\n\n/* Make code block captions be nicely integrated */\n.code-block-caption\n display: flex\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n border-radius: 0.25rem\n border-bottom-left-radius: 0\n border-bottom-right-radius: 0\n font-weight: 300\n border-bottom: 1px solid\n\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n border-color: var(--color-background-border)\n\n + div[class]\n margin-top: 0\n pre\n border-top-left-radius: 0\n border-top-right-radius: 0\n\n// When `html_codeblock_linenos_style` is table.\n.highlighttable\n width: 100%\n display: block\n tbody\n display: block\n\n tr\n display: flex\n\n // Line numbers\n td.linenos\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n padding: $code-spacing-vertical $code-spacing-horizontal\n padding-right: 0\n border-top-left-radius: 0.2rem\n border-bottom-left-radius: 0.2rem\n\n .linenodiv\n padding-right: $code-spacing-horizontal\n font-size: var(--code-font-size)\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n\n // Actual code\n td.code\n padding: 0\n display: block\n flex: 1\n overflow: hidden\n\n .highlight\n border-top-left-radius: 0\n border-bottom-left-radius: 0\n\n// When `html_codeblock_linenos_style` is inline.\n.highlight\n span.linenos\n display: inline-block\n padding-left: 0\n padding-right: $code-spacing-horizontal\n margin-right: $code-spacing-horizontal\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n","// Inline Footnote Reference\n.footnote-reference\n font-size: var(--font-size--small--4)\n vertical-align: super\n\n// Definition list, listing the content of each note.\n// docutils <= 0.17\ndl.footnote.brackets\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\n display: grid\n grid-template-columns: max-content auto\n dt\n margin: 0\n > .fn-backref\n margin-left: 0.25rem\n\n &:after\n content: \":\"\n\n .brackets\n &:before\n content: \"[\"\n &:after\n content: \"]\"\n\n dd\n margin: 0\n padding: 0 1rem\n\n// docutils >= 0.18\naside.footnote\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\naside.footnote > span,\ndiv.citation > span\n float: left\n font-weight: 500\n padding-right: 0.25rem\n\naside.footnote > p,\ndiv.citation > p\n margin-left: 2rem\n","//\n// Figures\n//\nimg\n box-sizing: border-box\n max-width: 100%\n height: auto\n\narticle\n figure, .figure\n border-radius: 0.2rem\n\n margin: 0\n :last-child\n margin-bottom: 0\n\n .align-left\n float: left\n clear: left\n margin: 0 1rem 1rem\n\n .align-right\n float: right\n clear: right\n margin: 0 1rem 1rem\n\n .align-default,\n .align-center\n display: block\n text-align: center\n margin-left: auto\n margin-right: auto\n\n // WELL, table needs to be stylised like a table.\n table.align-default\n display: table\n text-align: initial\n",".genindex-jumpbox, .domainindex-jumpbox\n border-top: 1px solid var(--color-background-border)\n border-bottom: 1px solid var(--color-background-border)\n padding: 0.25rem\n\n.genindex-section, .domainindex-section\n h2\n margin-top: 0.75rem\n margin-bottom: 0.5rem\n ul\n margin-top: 0\n margin-bottom: 0\n","ul,\nol\n padding-left: 1.2rem\n\n // Space lists out like paragraphs\n margin-top: 1rem\n margin-bottom: 1rem\n // reduce margins within li.\n li\n > p:first-child\n margin-top: 0.25rem\n margin-bottom: 0.25rem\n\n > p:last-child\n margin-top: 0.25rem\n\n > ul,\n > ol\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n\nol\n &.arabic\n list-style: decimal\n &.loweralpha\n list-style: lower-alpha\n &.upperalpha\n list-style: upper-alpha\n &.lowerroman\n list-style: lower-roman\n &.upperroman\n list-style: upper-roman\n\n// Don't space lists out when they're \"simple\" or in a `.. toctree::`\n.simple,\n.toctree-wrapper\n li\n > ul,\n > ol\n margin-top: 0\n margin-bottom: 0\n\n// Definition Lists\n.field-list,\n.option-list,\ndl:not([class]),\ndl.simple,\ndl.footnote,\ndl.glossary\n dt\n font-weight: 500\n margin-top: 0.25rem\n + dt\n margin-top: 0\n\n .classifier::before\n content: \":\"\n margin-left: 0.2rem\n margin-right: 0.2rem\n\n dd\n > p:first-child,\n ul\n margin-top: 0.125rem\n\n ul\n margin-bottom: 0.125rem\n",".math-wrapper\n width: 100%\n overflow-x: auto\n\ndiv.math\n position: relative\n text-align: center\n\n .headerlink,\n &:focus .headerlink\n display: none\n\n &:hover .headerlink\n display: inline-block\n\n span.eqno\n position: absolute\n right: 0.5rem\n top: 50%\n transform: translate(0, -50%)\n z-index: 1\n","// Abbreviations\nabbr[title]\n cursor: help\n\n// \"Problematic\" content, as identified by Sphinx\n.problematic\n color: var(--color-problematic)\n\n// Keyboard / Mouse \"instructions\"\nkbd:not(.compound)\n margin: 0 0.2rem\n padding: 0 0.2rem\n border-radius: 0.2rem\n border: 1px solid var(--color-foreground-border)\n color: var(--color-foreground-primary)\n vertical-align: text-bottom\n\n font-size: var(--font-size--small--3)\n display: inline-block\n\n box-shadow: 0 0.0625rem 0 rgba(0, 0, 0, 0.2), inset 0 0 0 0.125rem var(--color-background-primary)\n\n background-color: var(--color-background-secondary)\n\n// Blockquote\nblockquote\n border-left: 4px solid var(--color-background-border)\n background: var(--color-background-secondary)\n\n margin-left: 0\n margin-right: 0\n padding: 0.5rem 1rem\n\n .attribution\n font-weight: 600\n text-align: right\n\n &.pull-quote,\n &.highlights\n font-size: 1.25em\n\n &.epigraph,\n &.pull-quote\n border-left-width: 0\n border-radius: 0.5rem\n\n &.highlights\n border-left-width: 0\n background: transparent\n\n// Center align embedded-in-text images\np .reference img\n vertical-align: middle\n","p.rubric\n line-height: 1.25\n font-weight: bold\n font-size: 1.125em\n\n // For Numpy-style documentation that's got rubrics within it.\n // https://github.com/pradyunsg/furo/discussions/505\n dd &\n line-height: inherit\n font-weight: inherit\n\n font-size: var(--font-size--small)\n text-transform: uppercase\n","article .sidebar\n float: right\n clear: right\n width: 30%\n\n margin-left: 1rem\n margin-right: 0\n\n border-radius: 0.2rem\n background-color: var(--color-background-secondary)\n border: var(--color-background-border) 1px solid\n\n > *\n padding-left: 1rem\n padding-right: 1rem\n\n > ul, > ol // lists need additional padding, because bullets.\n padding-left: 2.2rem\n\n .sidebar-title\n margin: 0\n padding: 0.5rem 1rem\n border-bottom: var(--color-background-border) 1px solid\n\n font-weight: 500\n\n// TODO: subtitle\n// TODO: dedicated variables?\n",".table-wrapper\n width: 100%\n overflow-x: auto\n margin-top: 1rem\n margin-bottom: 0.5rem\n padding: 0.2rem 0.2rem 0.75rem\n\ntable.docutils\n border-radius: 0.2rem\n border-spacing: 0\n border-collapse: collapse\n\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n th\n background: var(--color-table-header-background)\n\n td,\n th\n // Space things out properly\n padding: 0 0.25rem\n\n // Get the borders looking just-right.\n border-left: 1px solid var(--color-table-border)\n border-right: 1px solid var(--color-table-border)\n border-bottom: 1px solid var(--color-table-border)\n\n p\n margin: 0.25rem\n\n &:first-child\n border-left: none\n &:last-child\n border-right: none\n\n // MyST-parser tables set these classes for control of column alignment\n &.text-left\n text-align: left\n &.text-right\n text-align: right\n &.text-center\n text-align: center\n",":target\n scroll-margin-top: 0.5rem\n\n@media (max-width: $full-width - $sidebar-width)\n :target\n scroll-margin-top: calc(0.5rem + var(--header-height))\n\n // When a heading is selected\n section > span:target\n scroll-margin-top: calc(0.8rem + var(--header-height))\n\n// Permalinks\n.headerlink\n font-weight: 100\n user-select: none\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\ndl dt,\np.caption,\nfigcaption p,\ntable > caption,\n.code-block-caption\n > .headerlink\n margin-left: 0.5rem\n visibility: hidden\n &:hover > .headerlink\n visibility: visible\n\n // Don't change to link-like, if someone adds the contents directive.\n > .toc-backref\n color: inherit\n text-decoration-line: none\n\n// Figure and table captions are special.\nfigure:hover > figcaption > p > .headerlink,\ntable:hover > caption > .headerlink\n visibility: visible\n\n:target >, // Regular section[id] style anchors\nspan:target ~ // Non-regular span[id] style \"extra\" anchors\n h1,\n h2,\n h3,\n h4,\n h5,\n h6\n &:nth-of-type(1)\n background-color: var(--color-highlight-on-target)\n // .headerlink\n // visibility: visible\n code.literal\n background-color: transparent\n\ntable:target > caption,\nfigure:target\n background-color: var(--color-highlight-on-target)\n\n// Inline page contents\n.this-will-duplicate-information-and-it-is-still-useful-here li :target\n background-color: var(--color-highlight-on-target)\n\n// Code block permalinks\n.literal-block-wrapper:target .code-block-caption\n background-color: var(--color-highlight-on-target)\n\n// When a definition list item is selected\n//\n// There isn't really an alternative to !important here, due to the\n// high-specificity of API documentation's selector.\ndt:target\n background-color: var(--color-highlight-on-target) !important\n\n// When a footnote reference is selected\n.footnote > dt:target + dd,\n.footnote-reference:target\n background-color: var(--color-highlight-on-target)\n",".guilabel\n background-color: var(--color-guilabel-background)\n border: 1px solid var(--color-guilabel-border)\n color: var(--color-guilabel-text)\n\n padding: 0 0.3em\n border-radius: 0.5em\n font-size: 0.9em\n","// This file contains the styles used for stylizing the footer that's shown\n// below the content.\n\nfooter\n font-size: var(--font-size--small)\n display: flex\n flex-direction: column\n\n margin-top: 2rem\n\n// Bottom of page information\n.bottom-of-page\n display: flex\n align-items: center\n justify-content: space-between\n\n margin-top: 1rem\n padding-top: 1rem\n padding-bottom: 1rem\n\n color: var(--color-foreground-secondary)\n border-top: 1px solid var(--color-background-border)\n\n line-height: 1.5\n\n @media (max-width: $content-width)\n text-align: center\n flex-direction: column-reverse\n gap: 0.25rem\n\n .left-details\n font-size: var(--font-size--small)\n\n .right-details\n display: flex\n flex-direction: column\n gap: 0.25rem\n text-align: right\n\n .icons\n display: flex\n justify-content: flex-end\n gap: 0.25rem\n font-size: 1rem\n\n a\n text-decoration: none\n\n svg,\n img\n font-size: 1.125rem\n height: 1em\n width: 1em\n\n// Next/Prev page information\n.related-pages\n a\n display: flex\n align-items: center\n\n text-decoration: none\n &:hover .page-info .title\n text-decoration: underline\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n svg.furo-related-icon,\n svg.furo-related-icon > use\n flex-shrink: 0\n\n color: var(--color-foreground-border)\n\n width: 0.75rem\n height: 0.75rem\n margin: 0 0.5rem\n\n &.next-page\n max-width: 50%\n\n float: right\n clear: right\n text-align: right\n\n &.prev-page\n max-width: 50%\n\n float: left\n clear: left\n\n svg\n transform: rotate(180deg)\n\n.page-info\n display: flex\n flex-direction: column\n overflow-wrap: anywhere\n\n .next-page &\n align-items: flex-end\n\n .context\n display: flex\n align-items: center\n\n padding-bottom: 0.1rem\n\n color: var(--color-foreground-muted)\n font-size: var(--font-size--small)\n text-decoration: none\n","// This file contains the styles for the contents of the left sidebar, which\n// contains the navigation tree, logo, search etc.\n\n////////////////////////////////////////////////////////////////////////////////\n// Brand on top of the scrollable tree.\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-brand\n display: flex\n flex-direction: column\n flex-shrink: 0\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n text-decoration: none\n\n.sidebar-brand-text\n color: var(--color-sidebar-brand-text)\n overflow-wrap: break-word\n margin: var(--sidebar-item-spacing-vertical) 0\n font-size: 1.5rem\n\n.sidebar-logo-container\n margin: var(--sidebar-item-spacing-vertical) 0\n\n.sidebar-logo\n margin: 0 auto\n display: block\n max-width: 100%\n\n////////////////////////////////////////////////////////////////////////////////\n// Search\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-search-container\n display: flex\n align-items: center\n margin-top: var(--sidebar-search-space-above)\n\n position: relative\n\n background: var(--color-sidebar-search-background)\n &:hover,\n &:focus-within\n background: var(--color-sidebar-search-background--focus)\n\n &::before\n content: \"\"\n position: absolute\n left: var(--sidebar-item-spacing-horizontal)\n width: var(--sidebar-search-icon-size)\n height: var(--sidebar-search-icon-size)\n\n background-color: var(--color-sidebar-search-icon)\n mask-image: var(--icon-search)\n\n.sidebar-search\n box-sizing: border-box\n\n border: none\n border-top: 1px solid var(--color-sidebar-search-border)\n border-bottom: 1px solid var(--color-sidebar-search-border)\n\n padding-top: var(--sidebar-search-input-spacing-vertical)\n padding-bottom: var(--sidebar-search-input-spacing-vertical)\n padding-right: var(--sidebar-search-input-spacing-horizontal)\n padding-left: calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size))\n\n width: 100%\n\n color: var(--color-sidebar-search-foreground)\n background: transparent\n z-index: 10\n\n &:focus\n outline: none\n\n &::placeholder\n font-size: var(--sidebar-search-input-font-size)\n\n//\n// Hide Search Matches link\n//\n#searchbox .highlight-link\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0\n margin: 0\n text-align: center\n\n a\n color: var(--color-sidebar-search-icon)\n font-size: var(--font-size--small--2)\n\n////////////////////////////////////////////////////////////////////////////////\n// Structure/Skeleton of the navigation tree (left)\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-tree\n font-size: var(--sidebar-item-font-size)\n margin-top: var(--sidebar-tree-space-above)\n margin-bottom: var(--sidebar-item-spacing-vertical)\n\n ul\n padding: 0\n margin-top: 0\n margin-bottom: 0\n\n display: flex\n flex-direction: column\n\n list-style: none\n\n li\n position: relative\n margin: 0\n\n > ul\n margin-left: var(--sidebar-item-spacing-horizontal)\n\n .icon\n color: var(--color-sidebar-link-text)\n\n .reference\n box-sizing: border-box\n color: var(--color-sidebar-link-text)\n\n // Fill the parent.\n display: inline-block\n line-height: var(--sidebar-item-line-height)\n text-decoration: none\n\n // Don't allow long words to cause wrapping.\n overflow-wrap: anywhere\n\n height: 100%\n width: 100%\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n &:hover\n background: var(--color-sidebar-item-background--hover)\n\n // Add a nice little \"external-link\" arrow here.\n &.external::after\n content: url('data:image/svg+xml,')\n margin: 0 0.25rem\n vertical-align: middle\n color: var(--color-sidebar-link-text)\n\n // Make the current page reference bold.\n .current-page > .reference\n font-weight: bold\n\n label\n position: absolute\n top: 0\n right: 0\n height: var(--sidebar-item-height)\n width: var(--sidebar-expander-width)\n\n cursor: pointer\n user-select: none\n\n display: flex\n justify-content: center\n align-items: center\n\n .caption, :not(.caption) > .caption-text\n font-size: var(--sidebar-caption-font-size)\n color: var(--color-sidebar-caption-text)\n\n font-weight: bold\n text-transform: uppercase\n\n margin: var(--sidebar-caption-space-above) 0 0 0\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n // If it has children, add a bit more padding to wrap the content to avoid\n // overlapping with the