Operator-approved ABI change (P2 stop-point). The WASM MLP is now MLPCore<DynamicStorage>: - nisps_ml_create(input, output, hidden[3], n, seed) honours its args; non-positive/null fall back to the historical 32→[10,14,18]→126, so every pre-P2 caller (manifold, worker, parity harness) stays bit-identical. Invalid/oversized dims (>4096) → null. - NEW nisps_ml_reshape(ml, in, out, hidden, n, spread): fresh net at the new dims, warm-started via nisps/ml/warm_start.hpp (overlapping region copied; rest keeps spread init); feedback controller re-created (state resets — reset-on-reshape modal is the front-end contract). Failure leaves the old net untouched. - nisps_ml_describe(ml, out): takes the handle; null reports defaults. - FeedbackController got the same storage split: algorithms in FeedbackControllerCore<FbStorage>; FixedFeedbackStorage keeps firmware/ tests source-identical via the old alias; DynamicFeedbackStorage (one arena) sizes to the runtime net. Firmware .text unchanged (122692). - MLHandle: per-instance scratch vectors; dropped the dead 2MB batch_out_scratch. - TS: types.ts decls (+_nisps_ml_reshape), wasm-iml re-describes the created instance, worker carries a shape-contract note for P2.3. Verified: ctest 4/4 incl. new warm-start grow/shrink test; reshape ABI smoke (dims honoured, overlap survives, invalid rejected, outputs bounded); parity PASS unchanged (2.4e-7); lint clean; manifold 9 unit + 20 e2e green; firmware .text 122692 (+0.30% vs pre-P2 baseline).
65 lines
2.5 KiB
C++
65 lines
2.5 KiB
C++
// nisps/ml/warm_start.hpp — copy overlapping weights between two MLPs of
|
||
// (possibly) different shapes.
|
||
//
|
||
// Used by the runtime-reshape path (one-core-engine-refactor P2): reshape =
|
||
// construct a NEW instance at the new dimensions, then warm-start it by
|
||
// copying every weight/bias whose (layer, node, input) coordinate exists in
|
||
// BOTH shapes. Weights outside the overlap keep the destination's fresh
|
||
// initialisation. Deterministic, allocation-free, works across storage
|
||
// policies (fixed→dynamic, dynamic→dynamic, fixed→fixed).
|
||
//
|
||
// Row-major layout per layer: w[node * fan_in + j]. The overlap is the
|
||
// top-left submatrix min(fan_out) × min(fan_in) plus the bias prefix
|
||
// min(fan_out).
|
||
|
||
#pragma once
|
||
|
||
#include <cstddef>
|
||
#include <span>
|
||
|
||
#include "../core/perf.hpp"
|
||
|
||
namespace nisps::ml {
|
||
|
||
namespace detail {
|
||
|
||
template <std::size_t L, typename DstMLP, typename SrcMLP>
|
||
NISPS_FORCE_INLINE void warm_start_copy_layer(DstMLP& dst, const SrcMLP& src) noexcept {
|
||
const std::size_t src_in = src.template fan_in_l<L>();
|
||
const std::size_t src_out = src.template fan_out_l<L>();
|
||
const std::size_t dst_in = dst.template fan_in_l<L>();
|
||
const std::size_t dst_out = dst.template fan_out_l<L>();
|
||
const std::size_t n_in = (src_in < dst_in) ? src_in : dst_in;
|
||
const std::size_t n_out = (src_out < dst_out) ? src_out : dst_out;
|
||
|
||
std::span<const float> sw = src.template weights_l<L>();
|
||
std::span<float> dw = dst.template weights_l<L>();
|
||
for (std::size_t node = 0; node < n_out; ++node) {
|
||
const std::size_t src_row = node * src_in;
|
||
const std::size_t dst_row = node * dst_in;
|
||
for (std::size_t j = 0; j < n_in; ++j) {
|
||
dw[dst_row + j] = sw[src_row + j];
|
||
}
|
||
}
|
||
|
||
std::span<const float> sb = src.template biases_l<L>();
|
||
std::span<float> db = dst.template biases_l<L>();
|
||
for (std::size_t node = 0; node < n_out; ++node) {
|
||
db[node] = sb[node];
|
||
}
|
||
}
|
||
|
||
} // namespace detail
|
||
|
||
// Copy the overlapping region of every layer from `src` into `dst`. Both
|
||
// must expose the MLP storage surface (fan_in_l/fan_out_l/weights_l/
|
||
// biases_l) — i.e. any MLPCore instantiation.
|
||
template <typename DstMLP, typename SrcMLP>
|
||
inline void warm_start_copy(DstMLP& dst, const SrcMLP& src) noexcept {
|
||
detail::warm_start_copy_layer<0u>(dst, src);
|
||
detail::warm_start_copy_layer<1u>(dst, src);
|
||
detail::warm_start_copy_layer<2u>(dst, src);
|
||
detail::warm_start_copy_layer<3u>(dst, src);
|
||
}
|
||
|
||
} // namespace nisps::ml
|