// PRISM_Q1_LORA_ADAMW_TEST_V1 #include "ggml.h" #include "ggml-alloc.h" #include "ggml-backend.h" #include "ggml-opt.h" // PRISM_Q1_LORA_INTERNAL_GRAPH_ACCESS_V1 #include "ggml-impl.h" #include #include #include #include #include #include #include #include #include #include #include #include // ============================================================ // TEST-ONLY COMPLETION OF THE PRIVATE OPTIMIZER CONTEXT // // This definition is pinned to Prism commit: // // 7529fdaaf99ffdc5ca71ace9c7409a56b27ad92f // // It permits this dedicated test to directly verify which tensors // receive AdamW m/v state and optimizer-step nodes. // ============================================================ struct ggml_opt_context { ggml_backend_sched_t backend_sched = nullptr; ggml_cgraph * allocated_graph = nullptr; ggml_cgraph * allocated_graph_copy = nullptr; struct ggml_context * ctx_static = nullptr; struct ggml_context * ctx_cpu = nullptr; struct ggml_context * ctx_compute = nullptr; struct ggml_context * ctx_copy = nullptr; ggml_backend_buffer_t buf_static = nullptr; ggml_backend_buffer_t buf_cpu = nullptr; std::mt19937 rng; enum ggml_opt_loss_type loss_type; enum ggml_opt_build_type build_type; enum ggml_opt_build_type build_type_alloc; struct ggml_tensor * inputs = nullptr; struct ggml_tensor * outputs = nullptr; struct ggml_tensor * labels = nullptr; struct ggml_tensor * loss = nullptr; struct ggml_tensor * pred = nullptr; struct ggml_tensor * ncorrect = nullptr; struct ggml_cgraph * gf = nullptr; struct ggml_cgraph * gb_grad = nullptr; struct ggml_cgraph * gb_opt = nullptr; bool static_graphs = false; bool eval_ready = false; std::vector grad_accs; std::vector grad_m; std::vector grad_v; int64_t iter = 1; int32_t opt_period = 1; int32_t opt_i = 0; bool loss_per_datapoint = false; ggml_opt_get_optimizer_params get_opt_pars = nullptr; void * get_opt_pars_ud = nullptr; struct ggml_tensor * opt_step_params = nullptr; enum ggml_opt_optimizer_type optimizer = GGML_OPT_OPTIMIZER_TYPE_ADAMW; }; // ============================================================ // CONSTANTS // ============================================================ static constexpr int64_t K = 256; static constexpr int64_t M = 16; static constexpr int64_t RANK = 4; static constexpr int64_t BATCH = 2; static constexpr float LORA_ALPHA = 8.0f; static constexpr float LORA_SCALE = LORA_ALPHA / static_cast(RANK); static constexpr int TRAINING_STEPS = 60; // ============================================================ // BASIC HELPERS // ============================================================ static std::vector read_f32( const ggml_tensor * tensor) { if (!tensor) { throw std::runtime_error( "Attempted to read a null F32 tensor."); } if (tensor->type != GGML_TYPE_F32) { throw std::runtime_error( std::string("Expected F32 tensor, got ") + ggml_type_name(tensor->type)); } std::vector result( ggml_nelements(tensor)); ggml_backend_tensor_get( tensor, result.data(), 0, ggml_nbytes(tensor)); return result; } static std::vector read_raw( const ggml_tensor * tensor) { if (!tensor) { throw std::runtime_error( "Attempted to read a null tensor."); } std::vector result( ggml_nbytes(tensor)); ggml_backend_tensor_get( tensor, result.data(), 0, result.size()); return result; } static double max_abs( const std::vector & values) { double result = 0.0; for (const float value : values) { result = std::max( result, std::abs( static_cast(value))); } return result; } static double max_abs_difference( const std::vector & a, const std::vector & b) { if (a.size() != b.size()) { throw std::runtime_error( "Vector size mismatch."); } double result = 0.0; for (size_t index = 0; index < a.size(); ++index) { result = std::max( result, std::abs( static_cast(a[index]) - static_cast(b[index]))); } return result; } static std::string join_strings( std::vector values) { std::sort( values.begin(), values.end()); std::string result; for (size_t index = 0; index < values.size(); ++index) { if (index > 0) { result += ","; } result += values[index]; } return result; } static void require( const bool condition, const std::string & message) { if (!condition) { throw std::runtime_error( message); } } // ============================================================ // CPU LORA REFERENCE // // GGML storage: // A = [K, RANK] // B = [RANK, M] // X = [K, BATCH] // ============================================================ static std::vector cpu_lora( const std::vector & x, const std::vector & a, const std::vector & b) { std::vector down( BATCH * RANK, 0.0f); std::vector up( BATCH * M, 0.0f); for (int64_t batch = 0; batch < BATCH; ++batch) { for (int64_t rank = 0; rank < RANK; ++rank) { float sum = 0.0f; for (int64_t k = 0; k < K; ++k) { sum += a[rank * K + k] * x[batch * K + k]; } down[ batch * RANK + rank] = sum; } } for (int64_t batch = 0; batch < BATCH; ++batch) { for (int64_t output = 0; output < M; ++output) { float sum = 0.0f; for (int64_t rank = 0; rank < RANK; ++rank) { sum += b[output * RANK + rank] * down[ batch * RANK + rank]; } up[ batch * M + output] = sum * LORA_SCALE; } } return up; } // PRISM_Q1_LORA_CPU_TARGET_V3 // // Exact CPU reference for Prism Q1_0: // // block size: 128 weights // storage: FP16 scale + 16 sign bytes // bit 1: +scale // bit 0: -scale // // This is used only to generate deterministic training labels. // It does not create a persistent expanded model tensor. static std::vector cpu_q1_base_output( const std::vector & packed, const std::vector & x) { constexpr int64_t qk = 128; constexpr size_t block_bytes = 18; static_assert( sizeof(ggml_fp16_t) == 2, "Unexpected ggml_fp16_t size."); const size_t blocks_per_row = static_cast(K / qk); const size_t row_bytes = blocks_per_row * block_bytes; require( packed.size() == static_cast(M) * row_bytes, "Unexpected packed Q1 buffer size."); require( x.size() == static_cast(K * BATCH), "Unexpected CPU input size."); std::vector result( static_cast(M * BATCH), 0.0f); for (int64_t batch = 0; batch < BATCH; ++batch) { for (int64_t output = 0; output < M; ++output) { float sum = 0.0f; const size_t row_offset = static_cast(output) * row_bytes; for (int64_t k = 0; k < K; ++k) { const size_t block_index = static_cast(k / qk); const int quant_index = static_cast(k % qk); const size_t block_offset = row_offset + block_index * block_bytes; ggml_fp16_t scale_fp16; std::memcpy( &scale_fp16, packed.data() + block_offset, sizeof(scale_fp16)); const float scale = GGML_FP16_TO_FP32(scale_fp16); const uint8_t packed_signs = packed[ block_offset + sizeof(ggml_fp16_t) + static_cast( quant_index >> 3)]; const int sign_bit = ( packed_signs >> (quant_index & 7) ) & 1; const float weight = sign_bit ? scale : -scale; sum += weight * x[ static_cast(batch * K + k)]; } result[ static_cast(batch * M + output) ] = sum; } } return result; } // ============================================================ // MAIN // ============================================================ int main() { try { ggml_log_set( nullptr, nullptr); ggml_backend_load_all(); const size_t device_count = ggml_backend_dev_count(); require( device_count > 0, "No GGML backend devices found."); std::vector devices; std::vector backends; size_t cuda_index = static_cast(-1); for (size_t index = 0; index < device_count; ++index) { ggml_backend_dev_t device = ggml_backend_dev_get(index); devices.push_back(device); const std::string device_name = ggml_backend_dev_name(device); if ( cuda_index == static_cast(-1) && device_name.find("CUDA") != std::string::npos ) { cuda_index = index; } ggml_backend_t backend = ggml_backend_dev_init( device, nullptr); require( backend != nullptr, "Failed to initialize backend: " + device_name); auto * reg = ggml_backend_dev_backend_reg( device); auto set_threads = reinterpret_cast< ggml_backend_set_n_threads_t>( ggml_backend_reg_get_proc_address( reg, "ggml_backend_set_n_threads")); if (set_threads) { set_threads( backend, std::max( 1u, std::thread::hardware_concurrency() / 2)); } backends.push_back( backend); } require( cuda_index != static_cast(-1), "CUDA backend was not found."); ggml_backend_t cuda_backend = backends[cuda_index]; std::vector scheduler_backends; scheduler_backends.push_back( cuda_backend); for (size_t index = 0; index < backends.size(); ++index) { if (index != cuda_index) { scheduler_backends.push_back( backends[index]); } } ggml_backend_sched_t scheduler = ggml_backend_sched_new( scheduler_backends.data(), nullptr, scheduler_backends.size(), GGML_DEFAULT_GRAPH_SIZE, false, true); require( scheduler != nullptr, "Failed to create backend scheduler."); std::cout << std::fixed << std::setprecision(10); std::cout << "CUDA_BACKEND=" << ggml_backend_name( cuda_backend) << "\n"; std::cout << "CUDA_DEVICE=" << ggml_backend_dev_description( devices[cuda_index]) << "\n"; // -------------------------------------------------------- // STATIC MODEL CONTEXT // -------------------------------------------------------- ggml_init_params static_params = { /*.mem_size =*/ 8 * ggml_tensor_overhead(), /*.mem_buffer =*/ nullptr, /*.no_alloc =*/ true, }; ggml_context * ctx_static = ggml_init( static_params); require( ctx_static != nullptr, "Failed to create static context."); ggml_tensor * x = ggml_new_tensor_2d( ctx_static, GGML_TYPE_F32, K, BATCH); ggml_set_name( x, "input_x"); ggml_tensor * base_weight = ggml_new_tensor_2d( ctx_static, GGML_TYPE_Q1_0, K, M); ggml_set_name( base_weight, "base_q1"); ggml_tensor * lora_a = ggml_new_tensor_2d( ctx_static, GGML_TYPE_F32, K, RANK); ggml_set_name( lora_a, "lora_a"); ggml_set_param( lora_a); ggml_tensor * lora_b = ggml_new_tensor_2d( ctx_static, GGML_TYPE_F32, RANK, M); ggml_set_name( lora_b, "lora_b"); ggml_set_param( lora_b); require( !(base_weight->flags & GGML_TENSOR_FLAG_PARAM), "Packed Q1 base was marked as a parameter."); require( !(x->flags & GGML_TENSOR_FLAG_PARAM), "Input X was marked as a parameter."); require( lora_a->flags & GGML_TENSOR_FLAG_PARAM, "LoRA A was not marked as a parameter."); require( lora_b->flags & GGML_TENSOR_FLAG_PARAM, "LoRA B was not marked as a parameter."); // -------------------------------------------------------- // COMPUTE GRAPH // -------------------------------------------------------- ggml_init_params compute_params = { /*.mem_size =*/ GGML_DEFAULT_GRAPH_SIZE * ggml_tensor_overhead() + 4 * ggml_graph_overhead(), /*.mem_buffer =*/ nullptr, /*.no_alloc =*/ true, }; ggml_context * ctx_compute = ggml_init( compute_params); require( ctx_compute != nullptr, "Failed to create compute context."); ggml_tensor * base_output = ggml_mul_mat( ctx_compute, base_weight, x); ggml_set_name( base_output, "base_output"); // Select the exact packed-Q1 × F32 training kernel. ggml_mul_mat_set_prec( base_output, GGML_PREC_F32); ggml_tensor * lora_down = ggml_mul_mat( ctx_compute, lora_a, x); ggml_set_name( lora_down, "lora_down"); ggml_tensor * lora_up = ggml_mul_mat( ctx_compute, lora_b, lora_down); ggml_set_name( lora_up, "lora_up"); ggml_tensor * lora_scaled = ggml_scale( ctx_compute, lora_up, LORA_SCALE); ggml_set_name( lora_scaled, "lora_scaled"); ggml_tensor * output = ggml_add( ctx_compute, base_output, lora_scaled); ggml_set_name( output, "q1_lora_output"); // -------------------------------------------------------- // ALLOCATE STATIC MODEL TENSORS ON CUDA // -------------------------------------------------------- ggml_backend_buffer_t model_buffer = ggml_backend_alloc_ctx_tensors( ctx_static, cuda_backend); require( model_buffer != nullptr, "Failed to allocate static model tensors."); // -------------------------------------------------------- // DETERMINISTIC INITIALIZATION // -------------------------------------------------------- std::vector host_x( K * BATCH); std::vector base_f32( K * M); std::vector target_a( K * RANK); std::vector target_b( RANK * M); std::vector initial_a( K * RANK); std::vector initial_b( RANK * M); for (size_t index = 0; index < host_x.size(); ++index) { host_x[index] = 0.70f * std::sin( 0.031f * static_cast(index)) + 0.20f * std::cos( 0.017f * static_cast(index)); } for (size_t index = 0; index < base_f32.size(); ++index) { base_f32[index] = 0.075f * std::sin( 0.013f * static_cast(index)) + 0.025f * std::cos( 0.007f * static_cast(index)); } for (size_t index = 0; index < target_a.size(); ++index) { target_a[index] = 0.040f * std::sin( 0.019f * static_cast(index + 1)); initial_a[index] = 0.60f * target_a[index] + 0.002f * std::cos( 0.011f * static_cast(index)); } for (size_t index = 0; index < target_b.size(); ++index) { target_b[index] = 0.050f * std::cos( 0.071f * static_cast(index + 1)); initial_b[index] = 0.60f * target_b[index] + 0.002f * std::sin( 0.053f * static_cast(index)); } std::vector importance( K, 1.0f); std::vector base_q1( ggml_nbytes( base_weight)); const size_t quantized_bytes = ggml_quantize_chunk( GGML_TYPE_Q1_0, base_f32.data(), base_q1.data(), 0, M, K, importance.data()); require( quantized_bytes == base_q1.size(), "Unexpected Q1 quantized byte count."); ggml_backend_tensor_set( x, host_x.data(), 0, ggml_nbytes(x)); ggml_backend_tensor_set( base_weight, base_q1.data(), 0, base_q1.size()); ggml_backend_tensor_set( lora_a, initial_a.data(), 0, ggml_nbytes(lora_a)); ggml_backend_tensor_set( lora_b, initial_b.data(), 0, ggml_nbytes(lora_b)); // -------------------------------------------------------- // OPTIMIZER CONTEXT // -------------------------------------------------------- ggml_opt_optimizer_params adamw = ggml_opt_get_default_optimizer_params( nullptr); adamw.adamw.alpha = 0.001f; adamw.adamw.beta1 = 0.9f; adamw.adamw.beta2 = 0.999f; adamw.adamw.eps = 1.0e-8f; adamw.adamw.wd = 0.0f; ggml_opt_params opt_params = ggml_opt_default_params( scheduler, GGML_OPT_LOSS_TYPE_MEAN_SQUARED_ERROR); opt_params.ctx_compute = ctx_compute; opt_params.inputs = x; opt_params.outputs = output; opt_params.opt_period = 1; opt_params.optimizer = GGML_OPT_OPTIMIZER_TYPE_ADAMW; opt_params.get_opt_pars = ggml_opt_get_constant_optimizer_params; opt_params.get_opt_pars_ud = &adamw; ggml_opt_context_t opt_context = ggml_opt_init( opt_params); require( opt_context != nullptr, "Failed to initialize AdamW context."); ggml_tensor * labels = ggml_opt_labels( opt_context); require( labels != nullptr, "MSE labels tensor was not created."); ggml_tensor * loss_tensor = ggml_opt_loss( opt_context); require( loss_tensor != nullptr, "Loss tensor was not created."); // -------------------------------------------------------- // DIRECT OPTIMIZER-STATE AUDIT // -------------------------------------------------------- require( opt_context->gf != nullptr, "Forward graph is missing."); require( opt_context->gb_opt != nullptr, "Optimizer graph is missing."); require( opt_context->grad_m.size() == static_cast( opt_context->gf->n_nodes), "AdamW m vector has unexpected size."); require( opt_context->grad_v.size() == static_cast( opt_context->gf->n_nodes), "AdamW v vector has unexpected size."); std::vector parameter_names; std::vector momentum_m_parameters; std::vector momentum_v_parameters; std::vector optimizer_step_parameters; std::map< std::string, ggml_tensor *> momentum_m; std::map< std::string, ggml_tensor *> momentum_v; bool base_has_momentum = false; for (int index = 0; index < opt_context->gf->n_nodes; ++index) { ggml_tensor * node = opt_context->gb_opt->nodes[index]; const std::string node_name = node->name; if ( node->flags & GGML_TENSOR_FLAG_PARAM ) { parameter_names.push_back( node_name); } if ( opt_context->grad_m[index] != nullptr ) { momentum_m_parameters.push_back( node_name); momentum_m[node_name] = opt_context->grad_m[index]; if (node_name == "base_q1") { base_has_momentum = true; } } if ( opt_context->grad_v[index] != nullptr ) { momentum_v_parameters.push_back( node_name); momentum_v[node_name] = opt_context->grad_v[index]; if (node_name == "base_q1") { base_has_momentum = true; } } } const std::string step_prefix = "AdamW step for "; for (int index = 0; index < opt_context->gb_opt->n_nodes; ++index) { const std::string node_name = opt_context ->gb_opt ->nodes[index] ->name; if ( node_name.rfind( step_prefix, 0) == 0 ) { optimizer_step_parameters.push_back( node_name.substr( step_prefix.size())); } } const std::string parameter_list = join_strings( parameter_names); const std::string momentum_m_list = join_strings( momentum_m_parameters); const std::string momentum_v_list = join_strings( momentum_v_parameters); const std::string optimizer_step_list = join_strings( optimizer_step_parameters); require( parameter_list == "lora_a,lora_b", "Unexpected parameter list: " + parameter_list); require( momentum_m_list == "lora_a,lora_b", "Unexpected AdamW m list: " + momentum_m_list); require( momentum_v_list == "lora_a,lora_b", "Unexpected AdamW v list: " + momentum_v_list); // PRISM_Q1_LORA_BEHAVIORAL_ADAMW_V2 // Optimizer-step node names are not a stable // public contract. Execution is validated below // through parameter changes, moment tensors, // frozen base bytes, and decreasing loss. require( !base_has_momentum, "Packed Q1 base received AdamW state."); require( momentum_m.count("lora_a") == 1 && momentum_m.count("lora_b") == 1 && momentum_v.count("lora_a") == 1 && momentum_v.count("lora_b") == 1, "Required LoRA moment tensors were not found."); std::cout << "PARAMETER_COUNT=" << parameter_names.size() << "\n"; std::cout << "PARAMETER_NAMES=" << parameter_list << "\n"; std::cout << "ADAMW_M_COUNT=" << momentum_m_parameters.size() << "\n"; std::cout << "ADAMW_M_PARAMETERS=" << momentum_m_list << "\n"; std::cout << "ADAMW_V_COUNT=" << momentum_v_parameters.size() << "\n"; std::cout << "ADAMW_V_PARAMETERS=" << momentum_v_list << "\n"; std::cout << "DIAGNOSTIC_NAMED_STEP_NODE_COUNT=" << optimizer_step_parameters.size() << "\n"; std::cout << "DIAGNOSTIC_NAMED_STEP_NODE_PARAMETERS=" << optimizer_step_list << "\n"; std::cout << "BASE_ADAMW_STATE=" << ( base_has_momentum ? "PRESENT" : "ABSENT") << "\n"; // -------------------------------------------------------- // BUILD TARGET LABELS WITHOUT READING GRAPH INTERMEDIATES // // Static optimizer graphs allocate a duplicated graph. // The original output intermediate can therefore have no // directly readable backend buffer. // // Generate the exact desired target on CPU instead: // // target = packed-Q1 base + target LoRA // -------------------------------------------------------- const std::vector base_reference = cpu_q1_base_output( base_q1, host_x); const std::vector target_lora = cpu_lora( host_x, target_a, target_b); require( base_reference.size() == target_lora.size(), "CPU target component size mismatch."); std::vector target_labels( static_cast(M * BATCH)); for (size_t index = 0; index < target_labels.size(); ++index) { target_labels[index] = base_reference[index] + target_lora[index]; } std::cout << "TARGET_GENERATION=CPU_PACKED_Q1_PLUS_LORA\n"; std::cout << "GRAPH_INTERMEDIATE_READS_FOR_TARGET=0\n"; // -------------------------------------------------------- // INITIAL LOSS // -------------------------------------------------------- ggml_opt_result_t result = ggml_opt_result_init(); ggml_opt_alloc( opt_context, false); ggml_backend_tensor_set( x, host_x.data(), 0, ggml_nbytes(x)); ggml_backend_tensor_set( labels, target_labels.data(), 0, ggml_nbytes(labels)); ggml_opt_eval( opt_context, result); double initial_loss = 0.0; ggml_opt_result_loss( result, &initial_loss, nullptr); require( std::isfinite( initial_loss), "Initial loss is not finite."); require( initial_loss > 1.0e-12, "Initial loss is too small for a useful test."); ggml_opt_result_reset( result); const std::vector base_before = read_raw( base_weight); const std::vector a_before = read_f32( lora_a); const std::vector b_before = read_f32( lora_b); // -------------------------------------------------------- // FIRST ADAMW STEP // -------------------------------------------------------- ggml_opt_alloc( opt_context, true); ggml_backend_tensor_set( x, host_x.data(), 0, ggml_nbytes(x)); ggml_backend_tensor_set( labels, target_labels.data(), 0, ggml_nbytes(labels)); ggml_opt_eval( opt_context, nullptr); const std::vector base_after_first = read_raw( base_weight); const std::vector a_after_first = read_f32( lora_a); const std::vector b_after_first = read_f32( lora_b); const double a_first_change = max_abs_difference( a_before, a_after_first); const double b_first_change = max_abs_difference( b_before, b_after_first); const bool base_first_unchanged = base_before == base_after_first; const double m_a_max = max_abs( read_f32( momentum_m.at( "lora_a"))); const double v_a_max = max_abs( read_f32( momentum_v.at( "lora_a"))); const double m_b_max = max_abs( read_f32( momentum_m.at( "lora_b"))); const double v_b_max = max_abs( read_f32( momentum_v.at( "lora_b"))); require( a_first_change > 1.0e-8, "LoRA A did not change on the first AdamW step."); require( b_first_change > 1.0e-8, "LoRA B did not change on the first AdamW step."); require( base_first_unchanged, "Packed Q1 base changed on the first step."); require( m_a_max > 0.0, "LoRA A AdamW m remained zero."); require( v_a_max > 0.0, "LoRA A AdamW v remained zero."); require( m_b_max > 0.0, "LoRA B AdamW m remained zero."); require( v_b_max > 0.0, "LoRA B AdamW v remained zero."); std::cout << "FIRST_STEP_A_MAX_CHANGE=" << a_first_change << "\n"; std::cout << "FIRST_STEP_B_MAX_CHANGE=" << b_first_change << "\n"; std::cout << "FIRST_STEP_BASE_CHANGED_BYTES=" << ( base_first_unchanged ? 0 : 1) << "\n"; std::cout << "LORA_A_M_MAX_ABS=" << m_a_max << "\n"; std::cout << "LORA_A_V_MAX_ABS=" << v_a_max << "\n"; std::cout << "LORA_B_M_MAX_ABS=" << m_b_max << "\n"; std::cout << "LORA_B_V_MAX_ABS=" << v_b_max << "\n"; // -------------------------------------------------------- // REMAINING TRAINING STEPS // -------------------------------------------------------- for (int step = 1; step < TRAINING_STEPS; ++step) { ggml_opt_alloc( opt_context, true); ggml_backend_tensor_set( x, host_x.data(), 0, ggml_nbytes(x)); ggml_backend_tensor_set( labels, target_labels.data(), 0, ggml_nbytes(labels)); ggml_opt_eval( opt_context, nullptr); } // -------------------------------------------------------- // FINAL LOSS // -------------------------------------------------------- ggml_opt_alloc( opt_context, false); ggml_backend_tensor_set( x, host_x.data(), 0, ggml_nbytes(x)); ggml_backend_tensor_set( labels, target_labels.data(), 0, ggml_nbytes(labels)); ggml_opt_eval( opt_context, result); double final_loss = 0.0; ggml_opt_result_loss( result, &final_loss, nullptr); const std::vector base_after = read_raw( base_weight); const std::vector a_after = read_f32( lora_a); const std::vector b_after = read_f32( lora_b); const double a_total_change = max_abs_difference( a_before, a_after); const double b_total_change = max_abs_difference( b_before, b_after); const bool base_unchanged = base_before == base_after; const double loss_ratio = final_loss / initial_loss; require( std::isfinite( final_loss), "Final loss is not finite."); require( final_loss < initial_loss, "Training loss did not decrease."); require( loss_ratio < 0.95, "Loss reduction was too small."); require( base_unchanged, "Packed Q1 base bytes changed during training."); require( a_total_change > 1.0e-7, "LoRA A did not receive a meaningful update."); require( b_total_change > 1.0e-7, "LoRA B did not receive a meaningful update."); std::cout << "TRAINING_STEPS=" << TRAINING_STEPS << "\n"; std::cout << "INITIAL_LOSS=" << initial_loss << "\n"; std::cout << "FINAL_LOSS=" << final_loss << "\n"; std::cout << "LOSS_RATIO=" << loss_ratio << "\n"; std::cout << "TOTAL_A_MAX_CHANGE=" << a_total_change << "\n"; std::cout << "TOTAL_B_MAX_CHANGE=" << b_total_change << "\n"; std::cout << "BASE_CHANGED_BYTES=" << ( base_unchanged ? 0 : 1) << "\n"; std::cout << "PERSISTENT_EXPANDED_WEIGHT_BYTES=0\n"; // -------------------------------------------------------- // FINAL CONTRACT // -------------------------------------------------------- std::cout << "CHECK_PARAMETER_FILTER=PASS\n"; std::cout << "CHECK_ADAMW_M_ONLY_AB=PASS\n"; std::cout << "CHECK_ADAMW_V_ONLY_AB=PASS\n"; std::cout << "CHECK_ADAMW_BEHAVIORAL_UPDATE_AB=PASS\n"; std::cout << "CHECK_FIRST_STEP_A_UPDATE=PASS\n"; std::cout << "CHECK_FIRST_STEP_B_UPDATE=PASS\n"; std::cout << "CHECK_MOMENTA_NONZERO=PASS\n"; std::cout << "CHECK_BASE_FROZEN=PASS\n"; std::cout << "CHECK_LOSS_DECREASE=PASS\n"; std::cout << "CHECK_NO_EXPANDED_BASE=PASS\n"; std::cout << "FINAL_STATUS=PASS\n"; // -------------------------------------------------------- // CLEANUP // -------------------------------------------------------- ggml_opt_result_free( result); ggml_opt_free( opt_context); ggml_backend_buffer_free( model_buffer); ggml_free( ctx_compute); ggml_free( ctx_static); ggml_backend_sched_free( scheduler); for (ggml_backend_t backend : backends) { ggml_backend_free( backend); } return 0; } catch ( const std::exception & exception ) { std::cerr << "FINAL_STATUS=FAIL\n"; std::cerr << "ERROR=" << exception.what() << "\n"; return 1; } }