docstr: Compute the contains_properly relationship between two GeoSeries.
  A feature A contains another feature B if no points of B lie in the
  exterior of A, and at least one point of the interior of B lies in the
  interior of A. This is the inverse of `within`.
docstr_tok: ["Compute","the","contains_properly","relationship","between","two","GeoSeries",".","A","feature","A","contains","another","feature","B","if","no","points","of","B","lie","in","the","exterior","of","A",",","and","at","least","one","point","of","the","interior","of","B","lies","in","the","interior","of","A",".","This","is","the","inverse","of","`","within","`","."]
code: def _compute_predicate(
      self,
      lhs: "GeoSeries",
      rhs: "GeoSeries",
      preprocessor_result: PreprocessorResult,
  ):
      """Compute the contains_properly relationship between two GeoSeries.
      A feature A contains another feature B if no points of B lie in the
      exterior of A, and at least one point of the interior of B lies in the
      interior of A. This is the inverse of `within`."""
      if not contains_only_polygons(lhs):
          raise TypeError(
              "`.contains` can only be called with polygon series."
          )
      mode = self._pip_mode(lhs, preprocessor_result.final_rhs)
      lhs_indices = lhs.index
      # Duplicates the lhs polygon for each point in the final_rhs result
      # that was computed by _preprocess. Will always ensure that the
      # number of points in the rhs is equal to the number of polygons in the
      # lhs.
      if mode == "pairwise":
          lhs_indices = preprocessor_result.point_indices
      pip_result = contains_properly(
          lhs[lhs_indices], preprocessor_result.final_rhs, mode=mode
      )
      # If the mode is pairwise or brute_force, we need to replace the
      # `pairwise_index` of each repeated polygon with the `part_index`
      # from the preprocessor result.
      if "pairwise_index" in pip_result.columns:
          pairwise_index_df = cudf.DataFrame(
              {
                  "pairwise_index": cp.arange(len(lhs_indices)),
                  "part_index": rhs.point_indices,
              }
          )
          pip_result = pip_result.merge(
              pairwise_index_df, on="pairwise_index"
          )[["part_index", "point_index"]]
      op_result = ContainsOpResult(pip_result, preprocessor_result)
      return self._postprocess(lhs, rhs, preprocessor_result, op_result)
  
imports: from typing import TypeVar
  import cupy
  import cudf
  from cuspatial.core.binpreds.basic_predicates import _basic_equals_all, _basic_intersects
  from cuspatial.core.binpreds.binpred_interface import BinPred, ContainsOpResult, ImpossiblePredicate, NotImplementedPredicate, PreprocessorResult
  from cuspatial.core.binpreds.contains import contains_properly
  from cuspatial.core.binpreds.contains_geometry_processor import ContainsGeometryProcessor
  from cuspatial.utils.binpred_utils import LineString, MultiPoint, Point, Polygon, _is_complex
  from cuspatial.utils.column_utils import contains_only_polygons, has_multipolygons
label_desc: Use image node_id 4 for calling the ContainsProperlyPredicate obj's underlying member method code with example usage: obj._compute_predicate(lhs, rhs, preprocessor_result) and returns: self