Class FluidRateFactors

java.lang.Object
jline.solvers.fluid.moments.FluidRateFactors

public final class FluidRateFactors extends Object
Per-coordinate service share of the closing fluid ODE and its analytic Jacobian. Java twin of the MATLAB ode_rates_closing_factors and of the rate-factor part of fluid_drift_jacobian.

The two live in one class on purpose. The drift the ODE integrates and the Jacobian the covariance (Lyapunov) equation reads must agree branch by branch, or the fixed point of the mean solve is not the point at which the covariance is linearised; keeping them in separate files is what makes that divergence possible. PassageTimeODE and FluidMomentTerms both delegate here, so the closing method and the moment-closure methods evaluate literally the same shares.

sigma2 is the per-station closure variance (0: first-order closure), lldscaling is sn.lldscaling (null: no load dependence) and covblk carries the per-station coordinate covariance blocks that close the capacity-share ratio at second order. All three leave the legacy code path bit-identical when absent, so the untouched methods are unaffected.

See Also:
  • Constructor Details

    • FluidRateFactors

      public FluidRateFactors(int M, int K, boolean[][] enabled, Matrix qIndices, Matrix Kic, Matrix nservers, Matrix w, SchedStrategy[] sched, Matrix lldscaling)
      Parameters:
      M - number of stations
      K - number of classes
      enabled - per-(station,class) service flag
      qIndices - per-(station,class) first state coordinate (0-based)
      Kic - per-(station,class) phase count
      nservers - per-station server count
      w - per-(station,class) DPS/GPS weight, 1 elsewhere
      sched - per-station scheduling strategy
      lldscaling - sn.lldscaling, null when the model has none
  • Method Details

    • isSupported

      public static boolean isSupported(SchedStrategy s)
      Scheduling disciplines with a branch below. Anything else would keep rates = x, i.e. be integrated as an INFINITE SERVER, and the answer would be wrong without any warning: on Delay(Z=1) -> Queue(c=1), N=4, exact Q2 = 3.0154, the fall-through returns 2.0000.
    • checkSupported

      public void checkSupported()
      Refuses any station whose discipline has no drift branch, naming it.

      Called before the drift is built rather than left to a fall-through: the metric readers accept SIRO as FCFS, so an unguarded SIRO station was integrated as INF while its metrics were read as if it shared the server, and every closing-family method returned a wrong answer silently.

    • driftKinkStation

      public int driftKinkStation(double[] x, double[] sigma2)
      First station whose population sits ON the saturation kink n_i = c_i of the first-order rate factor, or -1 when none does.

      With sigma2 = 0 the occupancy factor is min(n_i, c_i), whose derivative is the indicator of the unsaturated region: slope 1 below c_i, slope 0 above, and NO derivative at c_i itself. jacobian(double[], double[], jline.util.matrix.Matrix[]) resolves the tie with a strict n_i > c_i, as do the MATLAB and Python twins, so it silently returns the left slope there. That one-sided value is not the a.e. derivative, and which side a fixed point lands on is decided by the integrator's rounding residue rather than by the model: the same network at N=4 converges to n_i = c_i exactly here and to c_i + 3e-6 in Python, which flipped a Jacobian eigenvalue between -0.5 and 0 and hence the hyperbolicity verdict. Callers that need a differentiable drift must consult this instead of trusting the tie-break.

      Only the branches that take the indicator derivative can sit on a kink: a positive sigma2 or a load-dependent row makes the closure smooth, and an infinite server never saturates.

      Parameters:
      x - phase-resolved state
      sigma2 - per-station population variance, null or all-zero for the first-order closure
      Returns:
      the station index, or -1 when every station is off its kink
    • driftKinkStations

      public int[] driftKinkStations(double[] x, double[] sigma2)
      Every station sitting on the saturation kink, empty when none does. Same test as driftKinkStation(double[], double[]), which is its first element.
      Parameters:
      x - phase-resolved state
      sigma2 - per-station population variance, null or all-zero for the first-order closure
      Returns:
      the station indices, in increasing order
    • nudgedOffKink

      public double[] nudgedOffKink(double[] x, double[] sigma2, double rel)
      A copy of x with every station sitting on its saturation kink moved to c_i*(1+rel), i.e. strictly onto one side of it.

      The point of the copy is that the kink is a measure-zero event whose TIE-BREAK is arbitrary while its two one-sided Jacobians are both perfectly well defined. Evaluating the drift at the two nudged points is how a caller asks whether the side matters: if both sides agree on the verdict it wants, the tie-break is inconsequential and the reference's strict n_i > c_i may be used; if they disagree, the answer would be decided by the integrator's rounding residue instead of by the model.

      The station's coordinates are scaled together, so the phase mix and every other station are untouched.

      Parameters:
      x - phase-resolved state
      rel - signed relative offset from the server count, e.g. 1e-6
      Returns:
      the nudged state, or x itself when no station is on a kink
    • factors

      public Matrix factors(double[] x, double[] sigma2, Matrix[] covblk)
      Per-coordinate service share, before event indexing and before the constant rate factors are applied.
      Parameters:
      x - phase-resolved state
      sigma2 - per-station population variance, null or zero for the first-order closure
      covblk - per-station coordinate covariance blocks, null for the plug-in share
      Returns:
      one share per state coordinate
    • jacobian

      public Matrix jacobian(double[] x, double[] sigma2, Matrix[] covblk)
      Analytic Jacobian dg/dx of the rate factors, mirroring factors(double[], double[], jline.util.matrix.Matrix[]) branch by branch.

      With sigma2 = 0 the derivative of the occupancy factor is the indicator of the unsaturated region, i.e. the a.e. derivative of the first-order closure. With sigma2 > 0 it is the smooth derivative of the Gaussian closure.

      Parameters:
      x - phase-resolved state
      sigma2 - per-station population variance
      covblk - per-station coordinate covariance blocks
      Returns:
      the (n x n) Jacobian of the rate factors