our_knp/pets-knp/pets_readout.cpp

40 lines
3.9 KiB
C++

// Fixed host pooling is followed by a genuine AltAILIF/Delta learned classifier.
// Feature counts are repeated spikes in ONE packet; all weighted arithmetic is KNP.
#include <knp/framework/model_executor.h>
#include <knp/framework/network.h>
#include <knp/neuron-traits/altai_lif.h>
#include <knp/synapse-traits/delta.h>
#include <fstream>
#include <cstring>
#include <iostream>
#include <filesystem>
#include <vector>
#include <numeric>
#include <algorithm>
using P=knp::core::Population<knp::neuron_traits::AltAILIF>;
using R=knp::core::Projection<knp::synapse_traits::DeltaSynapse>;
using N=knp::neuron_traits::neuron_parameters<knp::neuron_traits::AltAILIF>;
template<class T>std::vector<T> read(const std::filesystem::path&p){std::ifstream f(p,std::ios::binary);if(!f)throw std::runtime_error("Missing file "+p.string());std::vector<char>b((std::istreambuf_iterator<char>(f)),{});if(b.size()%sizeof(T))throw std::runtime_error("Truncated file");std::vector<T>v(b.size()/sizeof(T));std::memcpy(v.data(),b.data(),b.size());return v;}
int main(int argc,char**argv)try{
if(argc!=5)throw std::runtime_error("counts.bin modeldir cpu|cuda results.csv");
auto x=read<int32_t>(argv[1]),w=read<int32_t>(std::filesystem::path(argv[2])/"weights.bin"),bias=read<int32_t>(std::filesystem::path(argv[2])/"bias.bin");
size_t nf=w.size();if((nf!=800&&nf!=256)||x.empty()||x.size()%nf||bias.size()!=1)throw std::runtime_error("Invalid dimensions");
int max_count=nf==256?64:640; int max_weight=nf==256?127:31;
int64_t bound=std::abs(int64_t(bias[0]));for(auto a:w){if(std::abs(int64_t(a))>max_weight)throw std::runtime_error("Invalid weight");bound+=int64_t(max_count)*std::abs(a);}if(bound>=16777216)throw std::runtime_error("Exact integer bound exceeded");
for(auto a:x)if(a<0||a>max_count)throw std::runtime_error("Invalid count");
std::vector<int>chunks;int64_t b=bias[0];while(b){int z=std::clamp<int64_t>(b,-65535,65535);chunks.push_back(z);b-=z;}
N n;n.activation_threshold_=1;n.negative_activation_threshold_=0;n.is_reset_=true;n.is_diff_=false;n.saturate_=true;n.do_not_save_=true;n.potential_reset_value_=0;n.potential_leak_=0;
P neurons([&](size_t){return n;},2);std::vector<R::Synapse>s;
for(int c=0;c<2;c++)for(size_t i=0;i<nf+chunks.size();i++){
int a=i<nf?w[i]:chunks[i-nf];if(c==0)a=-a;
if(a)s.push_back({{float(a),1,knp::synapse_traits::OutputType::EXCITATORY},i,size_t(c)});
}
R proj(knp::core::UID{false},neurons.get_uid(),[&](size_t i)->std::optional<R::Synapse>{return s[i];},s.size());auto pid=proj.get_uid(),nid=neurons.get_uid();
knp::framework::Network net;net.add_population(std::move(neurons));net.add_projection(std::move(proj));knp::framework::Model m(std::move(net));knp::core::UID ic,oc;m.add_input_channel(ic,pid);m.add_output_channel(oc,nid);
size_t count=x.size()/nf;auto gen=[&](knp::core::Step step){knp::core::messaging::SpikeData v;if(step>=count)return v;for(size_t i=0;i<nf;i++)if(w[i])for(int j=0;j<x[step*nf+i];j++)v.push_back(i);for(size_t j=0;j<chunks.size();j++)v.push_back(nf+j);return v;};
std::string mode=argv[3];if(mode!="cpu"&&mode!="cuda")throw std::runtime_error("Invalid backend");knp::framework::BackendLoader loader;knp::framework::ModelExecutor e(m,loader.load("/home/user/KNP/build/bin/libknp-"+std::string(mode=="cuda"?"altai-cuda":"cpu-single-threaded")+"-backend.so"),{{ic,gen}});
e.start([&](size_t t){return t<count+2;});std::vector<int>spikes(count*2);
for(auto&msg:e.get_loader().get_output_channel(oc).read_some_from_buffer(0,count+1)){if(msg.header_.send_time_==0)continue;size_t i=msg.header_.send_time_-1;if(i<count)for(auto c:msg.neuron_indexes_)++spikes[i*2+c];}
std::ofstream out;out.exceptions(std::ios::badbit|std::ios::failbit);out.open(argv[4]);out<<"row,cat,dog,prediction\n";
for(size_t i=0;i<count;i++)out<<i<<','<<spikes[i*2]<<','<<spikes[i*2+1]<<','<<(spikes[i*2+1]>spikes[i*2]?1:0)<<'\n';out.close();std::cout<<"READOUT "<<count<<" bound "<<bound<<'\n';
}catch(const std::exception&e){std::cerr<<e.what()<<'\n';return 1;}