class_docstr: Scalar Op that encapsulates a scalar loop operation.
  
  This Op can be used for the gradient of other Scalar Ops.
  It is much more restricted than `Scan` in that the entire inner graph
  must be composed of Scalar operations, and all inputs and outputs must be ScalarVariables.
  
  The pseudocode of the computation performed by this Op looks like the following:
  
  ```python
  def scalar_for_loop(fn, n_steps, init, update, constant):
      for i in range(n_steps):
          state = fn(*state, *constant)
      return state
  ```
  
  When an until condition is present it behaves like this:
  
  ```python
  def scalar_while_loop(fn, n_steps, init, update, constant):
      # If n_steps <= 0, we skip the loop altogether.
      # This does not count as a "failure"
      done = True
  
      for i in range(n_steps):
          *state, done = fn(*state, *constant)
          if done:
              break
  
      return *state, done
  ```
class_docstr_tok: ["Scalar","Op","that","encapsulates","a","scalar","loop","operation",".","This","Op","can","be","used","for","the","gradient","of","other","Scalar","Ops",".","It","is","much","more","restricted","than","`","Scan","`","in","that","the","entire","inner","graph","must","be","composed","of","Scalar","operations",",","and","all","inputs","and","outputs","must","be","ScalarVariables",".","The","pseudocode","of","the","computation","performed","by","this","Op","looks","like","the","following",":","``","`","python","def","scalar_for_loop","(","fn",",","n_steps",",","init",",","update",",","constant",")",":","for","i","in","range","(","n_steps",")",":","state","=","fn","(","*","state",",","*","constant",")","return","state","``","`","When","an","until","condition","is","present","it","behaves","like","this",":","``","`","python","def","scalar_while_loop","(","fn",",","n_steps",",","init",",","update",",","constant",")",":","#","If","n_steps","<","=","0",",","we","skip","the","loop","altogether",".","#","This","does","not","count","as","a","``","failure","''","done","=","True","for","i","in","range","(","n_steps",")",":","*","state",",","done","=","fn","(","*","state",",","*","constant",")","if","done",":","break","return","*","state",",","done","``","`"]
code: def fgraph(self):
      if hasattr(self, "_fgraph"):
          return self._fgraph
      # fgraph cannot be a property of the base class because it messes up with C caching.
      # We also need a `FunctionGraph(clone=True)` (default) according to an old comment
      fgraph = FunctionGraph(self.inputs, self.outputs)
      self._fgraph = fgraph
      return self._fgraph
  
imports: from collections.abc import Sequence
  from itertools import chain
  from typing import Optional
  from pytensor.compile import rebuild_collect_shared
  from pytensor.graph.basic import Constant, Variable, clone
  from pytensor.graph.fg import FunctionGraph
  from pytensor.scalar.basic import ScalarInnerGraphOp, as_scalar
label_desc: Use image node_id 4 for calling the ScalarLoop obj's underlying member method code with example usage: obj.fgraph() and returns: self, self