Commit e790723
chore: timeSeries deep forecasters: correct autodiff training (GradientTape + IEngine) + GPU-residency seam (#1841)
* fix: fix NHiTS backward pass: train via GradientTape autodiff so the model actually learns
NHiTSModel.TrainCore looped over ForwardWithGradients/ApplyGradients, but
ForwardWithGradients built an EMPTY gradient dictionary ("// Backward removed")
so ApplyGradients updated nothing — training was a no-op that returned an
untrained model.
Fix (mirrors the working NBEATSModel path):
- Re-express the stack MLP forward under GradientTape<T> using IEngine tensor
ops (NHiTSStackTensor.ForwardTape: TensorPermute / TensorMatMul /
TensorBroadcastAdd / ReLU). Autodiff now produces gradients for every
weight/bias and AdamOptimizer.Step applies them. The forward is fully
GPU-dispatchable (the point of the residency campaign).
- Register every weight/bias via RegisterTrainableParameter so
TapeTrainingStep.CollectParameters picks them up.
- TrainCore: z-normalize the series, build [B,L]->[B,H] windowed batches,
run tape forward over all stacks (multi-rate avg-pooled inputs), MSE loss,
tape.ComputeGradients + optimizer.Step. Denormalize at inference.
- Fix a train/inference mismatch: inference ForwardInternal used a hand-rolled
scalar 2D-indexer matmul that disagreed with the tape path (~3500x off),
producing garbage predictions from correctly-trained weights. It now uses the
same Engine.Tensor* ops as ForwardTape, so a trained model's inference output
matches what training optimized.
- Add best-checkpoint / early-stopping restore: snapshot params at the best
epoch and restore at the end, so late-epoch mini-batch Adam noise cannot
degrade the returned model.
- Remove the dead hand-derived Backward/UpdateParameter/GetParameterNames and
the unused activation caches.
Public API and NHiTSOptions are unchanged.
Verification on a noisy sinusoid+trend series (default hyperparameters,
lookback 20 / horizon 8 / 3 stacks): windowed forecast MSE 287.08 -> 18.44
(ratio 0.064, well under 0.5), beats the repeat-last-value baseline (21.85),
and probe forecasts track actuals closely (e.g. 18.13 vs 18.16, 16.87 vs
17.07). Before this change the model returned untrained output.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: batched GradientTape + IEngine forward (autodiff backward, GPU-dispatchable)
Re-express InformerModel<T> training with automatic differentiation and batched
Engine.Tensor* ops, mirroring the NBEATSModel / NHiTSModel tape pattern, so autodiff
produces the whole backward pass (no hand-derived gradients) and the model runs through
the GPU engine.
Model (src/TimeSeries/InformerModel.cs):
- TrainCore: z-normalizes the series, stacks windows into a [B, L] batch, runs a batched
tape forward (ForwardBatch), MeanSquaredErrorLoss.ComputeTapeLoss, tape.ComputeGradients,
AdamOptimizer.Step. Supervises the full H-step horizon per sample; snapshots/restores
best-epoch parameters.
- ForwardBatch: batched [B,L,1] -> [B,H] forward built entirely from Engine.* ops
(embedding, multi-head scaled-dot-product attention via ScaledDotProductAttention with the
[B,H,S,headDim] layout, LayerNorm, ReLU FFN, batched distilling conv+maxpool, generative
decoder, per-position output projection). Token-wise ops run on a flattened [B*S, d] matrix.
- ATTENTION SIMPLIFICATION: replaced ProbSparse self-attention with full multi-head
scaled-dot-product attention. ProbSparse's top-u query selection is a data-dependent host
gather that neither batches nor differentiates cleanly, and for these sequence lengths it
already reduces to full attention. Self-attention distilling is retained (now trained:
its conv weights were added to CollectTrainableParameters).
- Inference (ForwardEngine/PredictSingle/Predict/ForecastHorizon) uses the SAME batched
Engine ops (B=1), normalizing the input and denormalizing the forecast — no scalar/tape
divergence. Normalization stats are serialized for round-trip.
- Removed the dead hand-derived gradient/scalar code: ComputeGradients/ForwardWithCache/
EmbedInput/CreateDecoderInput/ComputeOutput/AccumulateGradients/ApplyGradients and the
scalar Forward/Backward/ProbSparse/FeedForward/attention in the encoder/decoder/distilling
layer classes (~630 lines).
Public API and InformerOptions unchanged.
testconsole: informer-verify (double CPU correctness) and informer-gpu (float32 GPU util)
harnesses + Program dispatch entries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(timeseries): gPU-resident training seam for NBEATS via compiled fused plan
Adds a reusable GPU-residency path for the tensorized TimeSeries forecasters
and wires NBEATSModel<float> to it, following NeuralNetworkBase's compiled
fused-optimizer template.
TimeSeriesModelBase<T>:
- CanTrainOnGpu guard (float + DirectGpuTensorEngine + compilation enabled),
mirroring NeuralNetworkBase.CanTrainOnGpu.
- TryFusedResidentStep(...): thin reusable wrapper over
CompiledTapeTrainingStep<T>.TryStepWithFusedOptimizer that compiles
forward+backward+Adam into a single on-device graph (weights, activations and
Adam moments stay resident; no per-op host<->device round-trip). Applies
gradient clipping (maxGradNorm=1.0) to match the eager Adam path.
NBEATSModel<T>:
- TryTrainGpuResident: runs the doubly-residual stack through the fused
compiled plan with a constant batch shape (required for capture/replay).
- Correctness-first validation gate: the resident run is kept only if it
improves validation MSE over the untrained baseline; on divergence or
no-improvement it re-initializes the blocks and returns false so TrainCore
falls back to the eager tape path. Gated to epoch-bounded mode so a rejected
attempt can't consume a wall-clock training budget.
- LastRunEpochLosses / LastRunUsedGpuResidentPath instrumentation (both paths).
Behavior:
- The double/CPU training path is unchanged (CanTrainOnGpu is float+GPU only);
verified NBEATS still trains cleanly on CPU (normalized loss 0.44 -> 0.0004).
- Float+GPU: the eager tape already dispatches every op to the CUDA engine; the
resident attempt is validated and safely falls back to eager when the compiled
plan doesn't reproduce eager gradients.
Known limitation (precise blocker): on the currently-linked Tensors build the
compiled fused plan does not reliably reproduce eager gradients for NBEATS's
per-layer TensorPermute + TensorBroadcastAdd op graph, so the resident attempt
typically validation-rejects for NBEATS and hands off to eager. NBEATS is also
host-bound (small per-op tensors), which caps GPU occupancy. The seam is retained
for TimeSeries models whose op graphs the compiler handles faithfully.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(timeseries): gPU-resident training seam for NHiTS via compiled fused plan
Mirrors NBEATSModel.TryTrainGpuResident so N-HiTS routes forward + backward +
Adam through a single on-device compiled plan (weights / activations / Adam
moments resident across every step) when float + DirectGpuTensorEngine +
compilation are all live.
* PoolBatchedTape: batched, tape-recordable average pooling via Reshape +
ReduceMean. [B, L] -> [B, L/k]. Requires L % k == 0; returns null (fused
path unsupported) for configs where the pool size doesn't divide the
lookback cleanly, so callers cleanly fall back to the eager scalar pool.
* RunForwardBatched: full multi-rate stack on-tape via per-stack
PoolBatchedTape + ForwardTape + sum. Single [B, L] input suits the
fused step's constant-shape replay contract.
* TryTrainGpuResident: NBEATS-parallel structure — pre-MSE baseline, batch
loop through TryFusedResidentStep, divergence guard, validation gate.
On reject the stacks are re-initialized so the eager fallback starts clean.
* LastRunUsedGpuResidentPath: telemetry flag on the model instance.
* TrainCore now attempts the resident path first (epoch-bounded only, per
the same wall-clock hazard NBEATS docs) and falls through to the existing
eager tape loop on failure.
Builds green net471 / net8.0 / net10.0. Correctness gate rejects the run
if it doesn't improve validation MSE by 2%, guaranteeing the resident path
can never ship worse weights than eager.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(timeseries): gPU-resident training seam for Informer via compiled fused plan
Same seam NBEATSModel + NHiTSModel use. Informer's forward is SDPA + LayerNorm
+ FFN + distilling — a very different op graph from NBEATS's Permute +
BroadcastAdd chain that trips the divergence guard on the linked Tensors
build, so the compiled fused plan should engage cleanly here even while
NBEATS keeps falling back.
* CollectTrainableLayers: flat ITrainableLayer<T> list over encoder +
distilling + decoder layers (all LayerBase-derived). Needed by
TryFusedResidentStep to ZeroGrad per step.
* ValidationMseGpu: 256-window baseline for the accept/reject gate.
* TryTrainGpuResident: NBEATS-parallel structure — pre-MSE, batch loop
through TryFusedResidentStep, divergence guard, validation gate.
On reject calls InitializeModel() so the eager fallback starts clean.
* LastRunUsedGpuResidentPath telemetry flag.
* TrainCore attempts the resident path first (epoch-bounded only, same
rationale as NBEATS/NHiTS) and falls through to the existing eager
tape+optimizer loop on failure.
Builds green net471 / net8.0 / net10.0. This completes wiring for the three
tape-based deep forecasters currently on the PR branch (NBEATS + NHiTS +
Informer). Autoformer / DeepAR / TFT branches referenced in the PR body
haven't landed on the remote yet — will be wired the same way when they do.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(tft): tape-train Temporal Fusion Transformer with batched IEngine ops
Re-express the TFT forward pass entirely with GradientTape + batched
Engine.Tensor* ops so autodiff produces the backward pass (no hand-derived
gradients) and every op is GPU-dispatchable — matching the NHiTS/Informer
tape conversions.
- ForwardBatch: [B,L]->[B,H] point forecast via embedding + positional
encoding, softmax-gated variable selection (GRN), static-enrichment GRN,
interpretable multi-head ScaledDotProductAttention, post-attention gated
skip (GRN), mean-pool + forecast head.
- GatedResidualNetwork rewritten batched/tape-safe (Engine ELU/Sigmoid/
LayerNorm with learned gamma/beta); removes scalar NumOps activation loops.
- TrainCore: batched tape forward -> MSE -> ComputeGradients -> Adam.Step,
z-normalized windows, best-epoch snapshot/restore. Inference (ForwardEngine)
uses the identical ops.
- Simplifications (documented): quantile/pinball head -> single point (mean)
MSE forecast; sequential LSTM -> positional encodings + attention; univariate
variable selection -> learned per-channel softmax gate. GRN + variable
selection + interpretable attention retained.
- Remove dead scalar VariableSelectionNetwork.cs. Public API and
TemporalFusionTransformerOptions unchanged.
Verify (standalone harness, double, sinusoid+trend series, 1100 pts, 40 ep):
MSE before 205.45 -> after 0.472 (0.23% of init, <0.5x); beats naive
repeat-last baseline 39.09 by ~83x.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(autoformer): tape-based automatic-differentiation training (no hand-derived gradients)
Convert AutoformerModel<T> training to a GradientTape + IEngine tensor-op
forward so autodiff produces the entire backward pass and every op is
GPU-dispatchable (matching the NHiTS/Informer conversions).
- ForwardCore: whole forward (embedding, series-decomposition moving-avg
trend/seasonal split, encoder/decoder, output projection) is now built
from Engine.Tensor* ops. The DEFINING series decomposition is kept, and
a tape-differentiable time-delay auto-correlation (R(lag) spectrum ->
top-k softmax -> rolled-value aggregation) replaces the old scalar
FFT-style attention; only the top-k index SELECTION is non-differentiable
(as in the official implementation).
- TrainCore rewritten: z-normalize the series, build [L]->[H] windows,
mini-batch gradient by accumulating each sample's gradient across
separate (immediately-disposed) tapes, then one averaged Adam step per
batch; best-epoch params snapshotted and restored. Removed the output-bias
level seeding (normalization handles level).
- Inference (Predict/PredictSingle/PredictMultiple) uses the SAME ForwardCore
ops (normalize in, denormalize out) so there is no train/predict divergence.
- Removed the dead scalar/hand-derived code: ComputeGradientsMultiStep,
ComputeMovingAverageNode, TopologicalSort, Accumulate/ApplyGradients,
ForwardWithCache, AutoformerCache, the gradient-accumulator infrastructure,
the ComputeGradients override, and all per-layer scalar forward/gradient
methods (~1400 net lines removed). Layers now hold parameters + (de)serialization only.
- Normalization stats persisted in Serialize/DeserializeCore. Public API and
AutoformerOptions unchanged.
Adds a testconsole `autoformer-verify` verb (trains on a learnable noisy
sinusoid+trend, reports before/after horizon MSE vs a repeat-last baseline).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(deepar): tape-based BPTT training + GPU-dispatchable forward
Re-express DeepARModel<T> training with GradientTape<T> + Engine.Tensor* ops
so autodiff produces the full backward pass (no hand-derived gradients) and
the forward path is GPU-dispatchable, mirroring the NHiTS/Informer/NBEATS
conversions.
- Unroll the LSTM recurrence over the L-step lookback as tape-tracked engine
ops (gate matmuls via TensorMatMul/TensorBroadcastAdd, gates split with
TensorNarrow, Sigmoid/Tanh/TensorMultiply), carrying [H,B] column-major
hidden/cell state across steps under the tape. Two internal LayerBase
subclasses (DeepARLstmCellTape, DeepARGaussianHead) register every
weight/bias via RegisterTrainableParameter so TapeTrainingStep.CollectParameters
picks them up.
- TrainCore: batched teacher-forced forward -> loss -> tape.ComputeGradients
-> AdamOptimizer.Step, with per-epoch best-checkpoint snapshot/restore.
Inference (PredictDistribution) runs the identical Step ops (B=1) so there
is no scalar/tape divergence. Removed all hand-derived scalar gradient code
(ComputeGradients/ApplyGradients/BackwardInternal).
- Gaussian likelihood retained: mean trained on MSE (undivided gradient) and
the scale trained on the Gaussian NLL term over a StopGradient-ed mean, so
sigma cannot inflate to collapse the mean (verified failure mode). Mean is
emitted as a residual on the current observation (mu_t = x_t + delta(h_t)),
which keeps the model at the persistence prior at init and lets training
learn the one-step change.
- Public API + DeepAROptions unchanged; added read-only TrainingLossHistory.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(timeseries): fix review comments — visibility, serialization, input validation, checkpoint
Addresses CodeRabbit review comments on PR#1841 (the clear, non-design ones;
the GPU-resident gradient root cause is handled separately):
- API visibility (keep the public/external-subclass surface unchanged, per the
facade objective): TimeSeriesModelBase.CanTrainOnGpu and TryFusedResidentStep
are now `private protected` (still usable by in-assembly models, hidden from
external subclasses); NBEATS/NHiTS/Informer LastRunUsedGpuResidentPath and
NBEATS LastRunEpochLosses are now `internal` (visible to tests/serving via
InternalsVisibleTo). LastRunEpochLosses is exposed as an immutable snapshot
(AsReadOnly) so callers cannot mutate the backing list.
- InformerModel.InitializeModel is now idempotent — it clears _encoderLayers,
_distillingLayers and _decoderLayers before rebuilding. TryTrainGpuResident
re-invokes it on a rejected resident run; previously it APPENDED a second full
layer set, doubling the model and corrupting ParameterCount/ForwardBatch/the
forecast. Safe on the constructor path (lists already empty).
- NHiTSModel now validates external inputs: every PoolingKernelSize must be
positive (a zero divided by zero in the downsampled-length calc; a negative
gave an invalid length), and a training series shorter than
LookbackWindow + ForecastHorizon is rejected up front (an empty series divided
by zero in the mean pass; a short series trained silently on zero windows).
- NHiTSModel.SerializeCore/DeserializeCore now persist _normMean/_normStd. A
reloaded model previously kept the defaults (0/1) and denormalized forecasts
incorrectly, so it no longer matched the original trained model.
- NHiTS best-checkpoint selection now scores each epoch on its FROZEN
end-of-epoch weights (via the existing ValidationMse forward) instead of the
mean of pre-update batch losses. bestLoss now measures exactly the weights
bestSnapshot captures, so a late-diverging epoch can no longer be selected as
best. The added pass is forward-only (no backprop).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(timeseries): resident-path review fixes — holdout gate, dead-code, failed-step fallback
Addresses the remaining CodeRabbit comments on the N-BEATS / N-HiTS GPU-resident
path (the compiled-plan gradient root cause is fixed separately in AiDotNet.Tensors):
- [3] Accept/reject gate now validates on a time-ordered HOLDOUT. Both models
reserved their windows for BOTH resident optimization AND the pre/post MSE
gate, so a 2% training-loss improvement proved nothing about generalization.
Reserve the latest ~20% of windows as a holdout the optimizer never trains on;
train on the earlier windows; score preMse/postMse on the holdout alone.
- [4] Removed the unreachable time-bounded logic from NBEATSModel.TryTrainGpuResident.
TrainCore only calls it when MaxTrainingTimeSeconds <= 0, so timeBounded, the
stopwatch, and both timeout branches were always dead. The loop now uses
_options.Epochs directly and keeps the cancellation checks. (N-HiTS already had
no such dead code.)
- [5] A failed fused step after the plan engaged now diverges + breaks (→ the
gate reinitializes and hands off to eager) instead of silently `continue`-ing
the batch, which could leave a partially-executed resident run to be accepted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(nbeats): stop eager training divergence blowing up forecasts
On noisy / fat-tailed real series the eager tape-Adam training loop diverges:
the normalized training loss climbs instead of falling (measured 1.20 -> 1.55
over 15 epochs on SPY daily log-returns) and the final weights produce forecasts
blown up to ~hundreds of times the target scale (a walk-forward eval measured
N-BEATS prediction variance ~692x the target vs ~0.07-4x for Informer/DLinear).
Two coupled guards, both on vectorized Tensor/IEngine ops (no jagged arrays, no
per-element scalar loops):
1. Global-norm gradient clipping (Oreshkin et al. 2020). New
NBEATSModelOptions.GradientClipNorm (default 1.0). The global L2 norm is
computed with Engine.TensorSumOfSquares per gradient and the whole set is
scaled in place with Engine.TensorMultiplyScalarInPlace when it exceeds the
threshold.
2. Best-epoch-weights checkpointing. Adam's adaptive step makes clipping alone
insufficient, so the parameters from the lowest-loss epoch are kept (as
Tensor clones, refreshed via Engine.TensorMultiplyScalarInto) and restored
after training — inference never runs on a diverged tail.
After the fix a 6-fold walk-forward over SPY daily returns keeps every fold's
prediction/target std ratio in 0.18x-0.74x (worst max|pred| 0.049 vs a target
std of 0.011) — the ~692x blow-up is gone. Existing NBEATSModelTests: 15/15 green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(timeseries): resolve review comments — TFT quantile head, RandomHelper, facade, resident parity
Addresses the blocking review comments on the tape-converted time-series models:
- TemporalFusionTransformer: restore a REAL quantile head. The tape conversion had
collapsed the quantile/pinball head to a single MSE point forecast, so
PredictQuantiles returned identical (degenerate) bands. Now the head projects to
[H*Q] (quantile-major) and trains with the summed pinball loss over every
QuantileLevels level, so PredictQuantiles returns a genuine ordered spread
(verified: q0.1 < q0.5 < q0.9 across the horizon); the point forecast is the
median-level head. Also validates each quantile level is in (0,1) at construction.
- new Random(42) -> RandomHelper.CreateSeededRandom(42) in Informer/NBEATS/NHiTS
(deterministic + the project's crypto-secure helper), matching DLinear/Autoformer.
- DeepARModel.TrainingLossHistory: public IReadOnlyList (leaked the mutable backing
List) -> internal + .AsReadOnly(), matching NBEATSModel.LastRunEpochLosses and the
facade-only rule.
- NBEATS GPU-resident path: on AiDotNet.Tensors 0.112.0 (#759 fixed the
TensorPermute/TensorBroadcastAdd multi-consumer grad-buffer blowup, #764 the
resident-parameter mistrain) the resident run now trains faithfully — verified on
GPU that it lowers the eager-forward training MSE and improves held-out MSE, so the
correctness gate accepts it. Updated the stale "does not reliably reproduce eager
gradients" docs; the gate is retained as a generalization safety net.
- testconsole/AutoformerVerify: seeded RandomHelper + Environment.Exit(1) on
verification failure so a regression fails the process.
- Directory.Packages.props: AiDotNet.Tensors + Native 0.111.1 -> 0.112.0 (brings the
resident-param + N-BEATS grad-buffer fixes).
Real TFT tests (unit + TS model-family) + NBEATSModelTests: 46/46 green. The
Generated.* generic-NN tests feed TS models non-time-series data (1-point series) and
fail pre-existing regardless of these changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: franklinic <franklin@ivorycloud.com>1 parent 1ca524d commit e790723
14 files changed
Lines changed: 3110 additions & 4545 deletions
File tree
- src
- Models/Options
- TimeSeries
- TFT
- testconsole
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
229 | 229 | | |
230 | 230 | | |
231 | 231 | | |
232 | | - | |
233 | | - | |
234 | | - | |
235 | | - | |
236 | | - | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
237 | 243 | | |
238 | 244 | | |
239 | 245 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
| 56 | + | |
56 | 57 | | |
57 | 58 | | |
58 | 59 | | |
| |||
193 | 194 | | |
194 | 195 | | |
195 | 196 | | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
196 | 216 | | |
197 | 217 | | |
198 | 218 | | |
| |||
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
9 | | - | |
| 8 | + | |
| 9 | + | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
11 | 19 | | |
12 | 20 | | |
13 | | - | |
14 | 21 | | |
15 | 22 | | |
16 | 23 | | |
17 | 24 | | |
18 | 25 | | |
19 | | - | |
20 | | - | |
21 | 26 | | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
26 | 30 | | |
27 | 31 | | |
28 | | - | |
29 | | - | |
| 32 | + | |
| 33 | + | |
30 | 34 | | |
31 | 35 | | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
36 | 40 | | |
37 | | - | |
38 | | - | |
| 41 | + | |
| 42 | + | |
39 | 43 | | |
40 | | - | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
41 | 49 | | |
42 | 50 | | |
43 | 51 | | |
44 | 52 | | |
45 | | - | |
46 | | - | |
47 | 53 | | |
48 | 54 | | |
49 | 55 | | |
50 | | - | |
51 | | - | |
52 | | - | |
| 56 | + | |
| 57 | + | |
53 | 58 | | |
54 | 59 | | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
| 60 | + | |
63 | 61 | | |
64 | 62 | | |
65 | | - | |
| 63 | + | |
66 | 64 | | |
67 | 65 | | |
68 | 66 | | |
69 | 67 | | |
70 | 68 | | |
71 | 69 | | |
72 | | - | |
73 | 70 | | |
74 | 71 | | |
75 | 72 | | |
76 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
80 | | - | |
| 80 | + | |
| 81 | + | |
81 | 82 | | |
82 | | - | |
83 | | - | |
84 | | - | |
| 83 | + | |
85 | 84 | | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
| 85 | + | |
| 86 | + | |
96 | 87 | | |
97 | 88 | | |
98 | | - | |
| 89 | + | |
99 | 90 | | |
100 | 91 | | |
101 | | - | |
102 | | - | |
103 | | - | |
| 92 | + | |
| 93 | + | |
104 | 94 | | |
105 | 95 | | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
| 96 | + | |
| 97 | + | |
114 | 98 | | |
115 | | - | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
116 | 102 | | |
117 | 103 | | |
118 | | - | |
119 | | - | |
120 | | - | |
| 104 | + | |
121 | 105 | | |
122 | 106 | | |
123 | 107 | | |
124 | 108 | | |
125 | | - | |
126 | 109 | | |
127 | 110 | | |
128 | 111 | | |
129 | 112 | | |
130 | 113 | | |
131 | 114 | | |
132 | 115 | | |
| 116 | + | |
| 117 | + | |
133 | 118 | | |
134 | 119 | | |
135 | | - | |
| 120 | + | |
| 121 | + | |
136 | 122 | | |
137 | | - | |
138 | 123 | | |
139 | | - | |
140 | | - | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
| 124 | + | |
155 | 125 | | |
156 | | - | |
157 | | - | |
158 | | - | |
159 | | - | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | | - | |
165 | | - | |
166 | | - | |
167 | | - | |
168 | | - | |
169 | | - | |
170 | | - | |
171 | | - | |
172 | | - | |
173 | | - | |
174 | | - | |
175 | | - | |
176 | | - | |
177 | | - | |
178 | | - | |
179 | | - | |
180 | | - | |
181 | | - | |
182 | | - | |
183 | | - | |
184 | | - | |
185 | | - | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
196 | | - | |
197 | | - | |
198 | | - | |
199 | | - | |
200 | | - | |
201 | | - | |
202 | | - | |
203 | | - | |
204 | | - | |
205 | | - | |
206 | | - | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
| 126 | + | |
214 | 127 | | |
215 | 128 | | |
216 | 129 | | |
| |||
219 | 132 | | |
220 | 133 | | |
221 | 134 | | |
| 135 | + | |
222 | 136 | | |
223 | 137 | | |
224 | | - | |
225 | 138 | | |
226 | 139 | | |
227 | 140 | | |
228 | | - | |
| 141 | + | |
229 | 142 | | |
230 | 143 | | |
231 | 144 | | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
232 | 155 | | |
0 commit comments