Skip to content

Conversation

@EastWest14
Copy link
Contributor

@EastWest14 EastWest14 commented May 1, 2025

#927

All Submissions:

  • Have you followed the guidelines in our Contributing document [docs/contribution.md]?
  • Have you checked to ensure there aren't other open [Pull Requests] for the same update/change?
  • Have you opened/linked the issue related to your pull request?
  • Have you used the tag [WIP] for on-going changes, and removed it when the pull request was ready?
  • When ready to merge, have you sent a comment pinging @pariterre in it?

New Feature Submissions:

  1. Does your submission pass the tests (if not please explain why this is intended)?
  2. Did you write a proper documentation (docstrings and ReadMe)
  3. Have you linted your code locally prior to submission (using the command: black . -l120 --exclude "external/*")?

Changes to Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new examples for your core changes, as applicable?
  • Have you written new tests for your core changes, as applicable?

This change is Reviewable

@EastWest14 EastWest14 changed the title [WIP]Convert gui [RTR]Convert gui May 2, 2025
@EastWest14 EastWest14 mentioned this pull request May 2, 2025
@EastWest14
Copy link
Contributor Author

@EveCharbie RTR

@EastWest14 EastWest14 changed the title [RTR]Convert gui [RTR]Convert gui to new types May 2, 2025
@EastWest14
Copy link
Contributor Author

@EveCharbie RTR also

Copy link
Collaborator

@EveCharbie EveCharbie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again !
Sorry I was not as explicit this time.
On Monday I will be on Linux, so let me know if you need help determining the type of some of them.

Reviewed 6 of 6 files at r1, all commit messages.
Reviewable status: all files reviewed, 65 unresolved discussions (waiting on @EastWest14)


bioptim/gui/serializable_class.py line 140 at r1 (raw file):

AnyDict

Code quote:

def serialize(self):

bioptim/gui/online_callback_abstract.py line 95 at r1 (raw file):

    @staticmethod
    def get_name_in(i: Int) -> Int:

Not you fault, but should have been a Str
def get_name_in(i: Int) -> Str:

Code quote:

def get_name_in(i: Int) -> Int:

bioptim/gui/online_callback_abstract.py line 148 at r1 (raw file):

    @abstractmethod
    def eval(self, arg: AnyIterable, enforce: Bool = False) -> IntIterable:

-> IntIterable | None (I don't know if you have an "optional" type for this case)

Code quote:

eval(self, arg: AnyIterable, enforce: Bool = False) -> IntIterable

bioptim/gui/online_callback_multiprocess.py line 36 at r1 (raw file):

        self.plot_process.start()

    def close(self):

-> None

Code quote:

def close(self):

bioptim/gui/online_callback_multiprocess.py line 39 at r1 (raw file):

        self.plot_process.kill()

    def eval(self, arg: AnyIterable, enforce: Bool = False) -> IntList:

-> IntList | None

Code quote:

eval(self, arg: AnyIterable, enforce: Bool = False) -> IntList:

bioptim/gui/online_callback_multiprocess.py line 79 at r1 (raw file):

            self._ocp: OcpSerializable = ocp
            self._plotter: PlotOcp = None
            self._update_time = 0.001

self._update_time: float = 0.001

Code quote:

self._update_time = 0.001

bioptim/misc/parameters_types.py line 19 at r1 (raw file):

AnyIterableOptional: TypeAlias = list[Any] | tuple[Any, ...] | None
AnyIterableOrRange: TypeAlias = slice | list | range
AnyIterableOrRangeOptional: TypeAlias = slice | list | range | None

Should we add tuple ?
AnyIterableOrRange: TypeAlias = slice | list | range | tuple

Code quote:

AnyIterableOrRange: TypeAlias = slice | list | range
AnyIterableOrRangeOptional: TypeAlias = slice | list | range | None

bioptim/gui/serializable_class.py line 34 at r1 (raw file):

    @classmethod
    def from_casadi_function(cls, casadi_function):

casadi_function: Function if it does not throw an error

Code quote:

def from_casadi_function(cls, casadi_function):

bioptim/gui/serializable_class.py line 47 at r1 (raw file):

        )

    def serialize(self):

-> dict[Str, Int]

Code quote:

def serialize(self):

bioptim/gui/serializable_class.py line 53 at r1 (raw file):

    @classmethod
    def deserialize(cls, data):

data: dict[Str, Int]

Code quote:

def deserialize(cls, data):

bioptim/gui/serializable_class.py line 58 at r1 (raw file):

        )

    def size_in(self, key: str) -> Int:

key: Str

Code quote:

def size_in(self, key: str) -> Int:

bioptim/gui/serializable_class.py line 79 at r1 (raw file):

        )

    def serialize(self):

-> AnyDict

Code quote:

def serialize(self):

bioptim/gui/serializable_class.py line 111 at r1 (raw file):

        )

    def serialize(self):

-> AnyDict

Code quote:

def serialize(self):

bioptim/gui/serializable_class.py line 164 at r1 (raw file):

        return cls(bounds=bounds)

    def serialize(self):

-> Anydict

Code quote:

def serialize(self):

bioptim/gui/serializable_class.py line 195 at r1 (raw file):

        )

    def check_and_adjust_dimensions(self, n_elements: int, n_shooting: int):

Int

Code quote:

def check_and_adjust_dimensions(self, n_elements: int, n_shooting: int):

bioptim/gui/serializable_class.py line 202 at r1 (raw file):

    @property
    def min(self):

-> NpArray

Code quote:

def min(self):

bioptim/gui/serializable_class.py line 206 at r1 (raw file):

    @property
    def max(self):

-> NpArray

Code quote:

def max(self):

bioptim/gui/serializable_class.py line 277 at r1 (raw file):

        )

    def serialize(self):

-> AnyDict

Code quote:

def serialize(self):

bioptim/gui/serializable_class.py line 312 at r1 (raw file):

        )

    def function(self, *args, **kwargs):

-> Callable

Code quote:

def function(self, *args, **kwargs):

bioptim/gui/serializable_class.py line 342 at r1 (raw file):

        )

    def serialize(self):

-> AnyDict

Code quote:

def serialize(self):

bioptim/gui/serializable_class.py line 430 at r1 (raw file):

        )

    def serialize(self):

-> AnyDict

Code quote:

def serialize(self):

bioptim/gui/serializable_class.py line 507 at r1 (raw file):

        )

    def serialize(self):

-> AnyDict

Code quote:

def serialize(self):

bioptim/gui/serializable_class.py line 583 at r1 (raw file):

        )

    def serialize(self):

-> AnyDict

Code quote:

def serialize(self):

bioptim/gui/online_callback_server.py line 103 at r1 (raw file):

        self._get_data_interval = 1.0
        self._update_plot_interval = 10
        self._force_redraw = False
    self._get_data_interval: Float = 1.0
    self._update_plot_interval: Int = 10
    self._force_redraw: Bool = False

Code quote:

        self._get_data_interval = 1.0
        self._update_plot_interval = 10
        self._force_redraw = False

bioptim/gui/online_callback_server.py line 108 at r1 (raw file):

        self._host = host if host else _DEFAULT_HOST
        self._port = port if port else _DEFAULT_PORT
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self._host: Str = host if host else _DEFAULT_HOST
    self._port: Int = port if port else _DEFAULT_PORT
    self._socket: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Code quote:

        self._host = host if host else _DEFAULT_HOST
        self._port = port if port else _DEFAULT_PORT
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

bioptim/gui/online_callback_server.py line 111 at r1 (raw file):

        self._plotter: PlotOcp = None

        self._should_send_ok_to_client_on_new_data = False

self._should_send_ok_to_client_on_new_data: Bool = False

Code quote:

self._should_send_ok_to_client_on_new_data = False

bioptim/gui/online_callback_server.py line 491 at r1 (raw file):

        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self._should_wait_ok_to_client_on_new_data = platform.system() == "Darwin"
    self._host: Str = host if host else _DEFAULT_HOST
    self._port: Int = port if port else _DEFAULT_PORT
    self._socket: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    self._should_wait_ok_to_client_on_new_data: Bool = platform.system() == "Darwin"

Code quote:

        self._host = host if host else _DEFAULT_HOST
        self._port = port if port else _DEFAULT_PORT
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self._should_wait_ok_to_client_on_new_data = platform.system() == "Darwin"

bioptim/gui/online_callback_server.py line 585 at r1 (raw file):

        self._socket.close()

    def eval(self, arg: AnyIterable, enforce: Bool = False) -> IntIterable:

-> IntIterable | None

Code quote:

def eval(self, arg: AnyIterable, enforce: Bool = False) -> IntIterable:

bioptim/gui/plot.py line 86 at r1 (raw file):

        linestyle: StrOptional = None,
        ylim: AnyIterableOptional = None,
        bounds: Bounds = None,

Bounds | None

Code quote:

bounds: Bounds = None,

bioptim/gui/plot.py line 148 at r1 (raw file):

        self.integration_rule = integration_rule
        self.parameters = parameters
        self.all_variables_in_one_subplot = all_variables_in_one_subplot

In general, could we want to type the extended attributes from a class the first time they appear in the init (like self.function: Callable = update_function).
Let me know if you need help understanding the type of each one :)

Code quote:

        self.function = update_function
        self.type = plot_type
        if axes_idx is None:
            self.phase_mappings = None  # Will be set later
        elif isinstance(axes_idx, (tuple, list)):
            self.phase_mappings = BiMapping(to_second=Mapping(axes_idx), to_first=Mapping(axes_idx))
        elif isinstance(axes_idx, BiMapping):
            self.phase_mappings = axes_idx
        else:
            raise RuntimeError("phase_mapping must be a list or a Mapping")
        self.legend = legend if legend is not None else ()
        self.combine_to = combine_to
        self.color = color
        self.linestyle = linestyle
        self.ylim = ylim
        self.bounds = bounds
        self.node_idx = node_idx  # If this is None, it is all nodes and will be initialize when we know the dimension of the problem
        self.label = label
        self.compute_derivative = compute_derivative
        if integration_rule == QuadratureRule.MIDPOINT or integration_rule == QuadratureRule.RECTANGLE_RIGHT:
            raise NotImplementedError(f"{integration_rule} has not been implemented yet.")
        self.integration_rule = integration_rule
        self.parameters = parameters
        self.all_variables_in_one_subplot = all_variables_in_one_subplot

bioptim/gui/plot.py line 229 at r1 (raw file):

        shooting_type: Shooting = Shooting.MULTIPLE,
        integrator: SolutionIntegrator = SolutionIntegrator.OCP,
        dummy_phase_times: list[FloatList] = None,

dummy_phase_times: list[FloatList] | None = None,

Code quote:

dummy_phase_times: list[FloatList] = None,

bioptim/gui/plot.py line 273 at r1 (raw file):

        self.variable_sizes = []
        self.show_bounds = show_bounds
        self.shooting_type = shooting_type

Same here

Code quote:

        self.ocp = ocp
        self.plot_options = self._initialize_plot_options()
        self.n_nodes = 0
        self.t = []
        self.t_integrated = []
        self.integrator = integrator
        self.axes = {}
        self.plots = []
        self.plots_vertical_lines = []
        self.plots_bounds = []
        self.all_figures = []
        self.automatically_organize = automatically_organize
        self.n_vertical_windows = None
        self.n_horizontal_windows = None
        self.top_margin = None
        self.height_step = None
        self.width_step = None
        self.custom_plots = {}
        self.variable_sizes = []
        self.show_bounds = show_bounds
        self.shooting_type = shooting_type

bioptim/gui/plot.py line 299 at r1 (raw file):

        }

    def _initialize_additional_plots(self):

-> None

Code quote:

def _initialize_additional_plots(self):

bioptim/gui/plot.py line 313 at r1 (raw file):

            create_conditioning_plots(self.ocp)

    def _update_time_vector(self, phase_times):

-> None

Code quote:

def _update_time_vector(self, phase_times):

bioptim/gui/plot.py line 324 at r1 (raw file):

            self.t.append(np.linspace(float(time[0][0]), float(time[-1][-1]), nlp.n_states_nodes))

    def _create_plots(self, only_initialize_variables: Bool):

-> None

Code quote:

def _create_plots(self, only_initialize_variables: Bool):

bioptim/gui/plot.py line 349 at r1 (raw file):

            self._process_plots_for_nlp(i, nlp, all_keys_across_phases, y_min_all, y_max_all, only_initialize_variables)

    def _initialize_variable_sizes(self):

-> list[dict[Str, Int]]
(I don't think we need a new type for this since it is a very particular case)

Code quote:

def _initialize_variable_sizes(self):

bioptim/gui/plot.py line 374 at r1 (raw file):

        return variable_sizes

    def _collect_all_keys_across_phases(self):

->StrList

Code quote:

def _collect_all_keys_across_phases(self):

bioptim/gui/plot.py line 386 at r1 (raw file):

    def _process_plots_for_nlp(
        self, i: Int, nlp, all_keys_across_phases, y_min_all, y_max_all, only_initialize_variables
    ):

Unfortunately, I cannot test the plots on my mac, but if you put a break point in this function while running examples/getting_started/pendulum.py -> sol.graphs(), you should be able to see what the type of these input arguments is.

Code quote:

    def _process_plots_for_nlp(
        self, i: Int, nlp, all_keys_across_phases, y_min_all, y_max_all, only_initialize_variables
    ):

bioptim/gui/plot.py line 408 at r1 (raw file):

            self._create_plots_for_variable(i, nlp, variable, axes, y_min_all, y_max_all, y_range_var_idx)

    def _setup_axes_for_variable(self, i: Int, nlp, variable):

variable -> Str ?
-> Tuple[ ?, ? ]

Code quote:

def _setup_axes_for_variable(self, i: Int, nlp, variable):

bioptim/gui/plot.py line 434 at r1 (raw file):

        return axes, nb_subplots

    def _calculate_max_subplots(self, variable):

variable: ?

Code quote:

def _calculate_max_subplots(self, variable):

bioptim/gui/plot.py line 450 at r1 (raw file):

        )

    def _initialize_custom_plot(self, i: Int, variable):

variable: ?

Code quote:

def _initialize_custom_plot(self, i: Int, variable):

bioptim/gui/plot.py line 457 at r1 (raw file):

            ]

    def _create_plots_for_variable(self, i: Int, nlp, variable, axes, y_min_all, y_max_all, y_range_var_idx):

Same here

Code quote:

 def _create_plots_for_variable(self, i: Int, nlp, variable, axes, y_min_all, y_max_all, y_range_var_idx):

bioptim/gui/plot.py line 534 at r1 (raw file):

        return y_min, y_max

    def _create_plot_for_axis(self, i: Int, nlp, variable, ax):

Same

Code quote:

def _create_plot_for_axis(self, i: Int, nlp, variable, ax):

bioptim/gui/plot.py line 553 at r1 (raw file):

            raise RuntimeError(f"{plot_type} is not implemented yet")

    def _get_plot_color(self, variable, i: Int, plot_type):

Same

Code quote:

def _get_plot_color(self, variable, i: Int, plot_type):

bioptim/gui/plot.py line 559 at r1 (raw file):

        )

    def _create_plot_type_plot(self, i: Int, t, ax, color, label):

Same

Code quote:

def _create_plot_type_plot(self, i: Int, t, ax, color, label):

bioptim/gui/plot.py line 577 at r1 (raw file):

        )

    def _create_plot_type_integrated(self, i: Int, ax, color, label):

Same

Code quote:

def _create_plot_type_integrated(self, i: Int, ax, color, label):

bioptim/gui/plot.py line 592 at r1 (raw file):

        self.plots.append([PlotType.INTEGRATED, i, plots_integrated])

    def _create_plot_type_step(self, i: Int, t, ax, variable, color, label):

Same

Code quote:

def _create_plot_type_step(self, i: Int, t, ax, variable, color, label):

bioptim/gui/plot.py line 604 at r1 (raw file):

        )

    def _create_plot_type_point(self, i: Int, t, ax, color, label, variable):

Same

Code quote:

def _create_plot_type_point(self, i: Int, t, ax, color, label, variable):

bioptim/gui/plot.py line 616 at r1 (raw file):

        )

    def _add_legend_to_axes(self, axes):

Same

Code quote:

def _add_legend_to_axes(self, axes):

bioptim/gui/plot.py line 621 at r1 (raw file):

            self._legend_without_duplicate_labels(ax)

    def _legend_without_duplicate_labels(self, ax):

Same

Code quote:

def _legend_without_duplicate_labels(self, ax):

bioptim/gui/plot.py line 628 at r1 (raw file):

            ax.legend(*zip(*unique))

    def _add_vertical_lines_and_bounds(self, i: Int, nlp, variable, axes, mapping_to_first_index):

Same

Code quote:

def _add_vertical_lines_and_bounds(self, i: Int, nlp, variable, axes, mapping_to_first_index):

bioptim/gui/plot.py line 637 at r1 (raw file):

                    self._add_bounds_to_plot(i, nlp, variable, ctr, ax, mapping_to_first_index)

    def _add_vertical_lines(self, ax):

Same

Code quote:

def _add_vertical_lines(self, ax):

bioptim/gui/plot.py line 643 at r1 (raw file):

            self.plots_vertical_lines.append(ax.axvline(float(time), **self.plot_options["vertical_lines"]))

    def _add_bounds_to_plot(self, i: Int, nlp, variable, ctr, ax, mapping_to_first_index):

Same

Code quote:

def _add_bounds_to_plot(self, i: Int, nlp, variable, ctr, ax, mapping_to_first_index):

bioptim/gui/plot.py line 665 at r1 (raw file):

        self.plots_bounds.append([ax.step(self.t[i], bounds_max, where="post", **self.plot_options["bounds"]), i])

    def _add_new_axis(self, variable: Str, nb: Int, n_rows: Int, n_cols: Int):

-> ?

Code quote:

def _add_new_axis(self, variable: Str, nb: Int, n_rows: Int, n_cols: Int):

bioptim/gui/plot.py line 706 at r1 (raw file):

        return axes

    def _organize_windows(self, n_windows: Int):

-> None

Code quote:

def _organize_windows(self, n_windows: Int):

bioptim/gui/plot.py line 724 at r1 (raw file):

            self.width_step = width / self.n_vertical_windows

    def _spread_figures_on_screen(self):

-> None

Code quote:

 def _spread_figures_on_screen(self):

bioptim/gui/plot.py line 743 at r1 (raw file):

                fig.tight_layout()

    def find_phases_intersections(self):

-> list[float]

Code quote:

def find_phases_intersections(self):

bioptim/gui/plot.py line 841 at r1 (raw file):

        ydata: AnyIterable,
        **args: AnyDict,
    ):

-> None

Code quote:

    def update_data(
        self,
        xdata: AnyIterable,
        ydata: AnyIterable,
        **args: AnyDict,
    ):

bioptim/gui/plot.py line 876 at r1 (raw file):

    def _compute_y_from_plot_func(
        self, custom_plot: CustomPlot, phase_idx: Int, time_stepwise, dt, x_decision, x_stepwise, u, p, a, d
    ) -> list[FloatIterableorNpArray]:

time_stepwise: np.ndarray
dt: np.ndarray
x_decision: np.ndarray
x_stepwise: np.ndarray
u: np.ndarray
p: np.ndarray
a: np.ndarray
d: np.ndarray
-> list[FloatIterableorNpArray] | None

Code quote:

    def _compute_y_from_plot_func(
        self, custom_plot: CustomPlot, phase_idx: Int, time_stepwise, dt, x_decision, x_stepwise, u, p, a, d
    ) -> list[FloatIterableorNpArray]:

bioptim/gui/plot.py line 931 at r1 (raw file):

    def _compute_values_at_nodes(
        self, custom_plot, phase_idx, time_stepwise, dt, x, u, p, a, d, get_numerical_timeseries
    ):

Same as the one abonve

Code quote:

    def _compute_values_at_nodes(
        self, custom_plot, phase_idx, time_stepwise, dt, x, u, p, a, d, get_numerical_timeseries
    ):

bioptim/gui/plot.py line 962 at r1 (raw file):

        return all_y

    def _get_penalty_node_data(self, custom_plot, idx, time_stepwise, x, u, p, a, get_numerical_timeseries):

Same

Code quote:

def _get_penalty_node_data(self, custom_plot, idx, time_stepwise, x, u, p, a, get_numerical_timeseries):

bioptim/gui/plot.py line 1005 at r1 (raw file):

        return t0, x_node, u_node, p_node, a_node, d_node

    def _format_integrated_plot_data(self, custom_plot, all_y):

Dont' what the types are, but should be added

Code quote:

def _format_integrated_plot_data(self, custom_plot, all_y):

bioptim/gui/plot.py line 1018 at r1 (raw file):

        return out

    def _format_standard_plot_data(self, custom_plot, all_y):

Same

Code quote:

def _format_standard_plot_data(self, custom_plot, all_y):

bioptim/gui/plot.py line 1029 at r1 (raw file):

        return out

    def _update_xdata(self, phase_times):

phase_times: list[FloatList]
-> None

Code quote:

def _update_xdata(self, phase_times):

bioptim/gui/plot.py line 1062 at r1 (raw file):

                    self.plots_vertical_lines[p * n + i].set_xdata([float(time), float(time)])

    def _update_ydata(self, ydata: AnyIterable):

-> None

Code quote:

def _update_ydata(self, ydata: AnyIterable):

@EveCharbie EveCharbie changed the title [RTR]Convert gui to new types Convert gui to new types May 2, 2025
@EastWest14 EastWest14 changed the title Convert gui to new types [RTR]Convert gui to new types May 4, 2025
@EastWest14 EastWest14 requested a review from EveCharbie May 4, 2025 22:24
@EastWest14
Copy link
Contributor Author

@EveCharbie Updated. Not sure of type type for Axis, updated the rest

Copy link
Collaborator

@EveCharbie EveCharbie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EastWest14 Thank you for this great work !
I have added a few commits, which pariterre should review before we merge (but he is still unavailable for the the next week at least).
FYI: I am going on vacation for a week, so I won't be available for reviewing. Have a nice week :)

Reviewed 6 of 6 files at r2, all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @EastWest14)


bioptim/gui/plot.py line 229 at r1 (raw file):

Previously, EveCharbie (Eve Charbonneau) wrote…

dummy_phase_times: list[FloatList] | None = None,

I've noticed you have a FloatListOptional, I think it is more appropriate


bioptim/gui/plot.py line 151 at r2 (raw file):

        self.ylim: DoubleFloatTuple | FloatList = ylim
        self.bounds: Bounds = bounds
        self.node_idx: AnyList = (

self.node_idx: IntIterable

Code quote:

self.node_idx: AnyList

EveCharbie
EveCharbie previously approved these changes May 5, 2025
Copy link
Collaborator

@EveCharbie EveCharbie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @EastWest14)

Copy link
Member

@pariterre pariterre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

Reviewed 4 of 4 files at r3, all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @EastWest14)

@pariterre
Copy link
Member

Looks good :) Thanks again for this work!

@pariterre pariterre merged commit 2180879 into pyomeca:master May 22, 2025
20 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants