Instructions to use ZibinDong/ActionCodec2 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ZibinDong/ActionCodec2 with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("ZibinDong/ActionCodec2", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download actioncodec2_kernels/grammar.cpp from ZibinDong/ActionCodec2: direct link, hf CLI and curl.
- Browser
- Download file 14.8 kB
-
https://huggingface.co/ZibinDong/ActionCodec2/resolve/ef300dff7f6ffa4e4e7eef5fdfef0fd684e7d573/actioncodec2_kernels/grammar.cpp
- Command line
-
hf download hf://ZibinDong/ActionCodec2@ef300dff7f6ffa4e4e7eef5fdfef0fd684e7d573/actioncodec2_kernels/grammar.cpp
-
curl -L -o grammar.cpp https://huggingface.co/ZibinDong/ActionCodec2/resolve/ef300dff7f6ffa4e4e7eef5fdfef0fd684e7d573/actioncodec2_kernels/grammar.cpp
14.8 kB
| // Coverage grammars and multi-component routing, including time-major streams. | |
| namespace py=pybind11; | |
| namespace { | |
| using State=std::vector<int64_t>; | |
| using States=std::vector<State>; | |
| class Grammar { | |
| public: | |
| int64_t horizon,vocabulary; | |
| virtual ~Grammar()=default; | |
| virtual State initial()const=0; | |
| virtual State advance(const State&,int64_t)const=0; | |
| virtual int64_t covered(const State&)const=0; | |
| virtual int64_t next_dimension(const State&)const=0; | |
| virtual bool allowed(const State&,int64_t)const=0; | |
| virtual std::shared_ptr<Grammar> clone(int64_t)const=0; | |
| py::array_t<bool> mask(const State& state)const { | |
| py::array_t<bool> result(vocabulary); | |
| py::gil_scoped_release release; | |
| for(int64_t token=0;token<vocabulary;++token)result.mutable_data()[token]=allowed(state,token); | |
| return result; | |
| } | |
| }; | |
| class SetGrammar:public Grammar { | |
| uint64_t full_; | |
| using Patterns=std::vector<std::vector<std::pair<int64_t,uint64_t>>>; | |
| std::shared_ptr<const std::vector<int64_t>> roots_; | |
| std::shared_ptr<const Patterns> patterns_; | |
| std::shared_ptr<const std::vector<bool>> scope_; | |
| public: | |
| SetGrammar(int64_t steps,uint64_t full,std::vector<int64_t> roots, | |
| std::vector<std::vector<std::pair<int64_t,uint64_t>>> patterns) | |
| :full_(full),roots_(std::make_shared<const std::vector<int64_t>>(std::move(roots))), | |
| patterns_(std::make_shared<const Patterns>(std::move(patterns))) { | |
| if(steps<1 || !full || roots_->size()!=patterns_->size())throw std::invalid_argument("invalid grammar metadata"); | |
| horizon=steps;vocabulary=roots_->size(); | |
| std::vector<bool> scopes; | |
| for(const auto& pattern:*patterns_) { | |
| bool scope=true; | |
| for(auto[offset,support]:pattern) { | |
| if(offset<0)throw std::invalid_argument("negative grammar pattern offset"); | |
| scope=scope && !(support & ~full_); | |
| } | |
| scopes.push_back(scope); | |
| } | |
| scope_=std::make_shared<const std::vector<bool>>(std::move(scopes)); | |
| } | |
| std::shared_ptr<Grammar> clone(int64_t steps)const override { | |
| if(steps<1)throw std::invalid_argument("horizon must be positive"); | |
| auto result=std::make_shared<SetGrammar>(*this);result->horizon=steps;return result; | |
| } | |
| State initial()const override{return State(horizon,0);} | |
| int64_t covered(const State& state)const override { | |
| if(static_cast<int64_t>(state.size())!=horizon)throw std::invalid_argument("invalid grammar state"); | |
| for(int64_t i=0;i<horizon;++i)if(static_cast<uint64_t>(state[i])!=full_)return i; | |
| return horizon; | |
| } | |
| int64_t next_dimension(const State& state)const override { | |
| int64_t front=covered(state);if(front==horizon)return -1; | |
| uint64_t missing=full_ & ~static_cast<uint64_t>(state[front]); | |
| int64_t d=0;while(!(missing & 1)){missing>>=1;++d;}return d; | |
| } | |
| bool allowed(const State& state,int64_t token)const override { | |
| if(token<0 || token>=vocabulary || !(*scope_)[token])return false; | |
| const int64_t front=covered(state); | |
| if(front==horizon || (*roots_)[token]!=next_dimension(state))return false; | |
| for(auto[offset,support]:(*patterns_)[token]) { | |
| if(front+offset>=horizon || (static_cast<uint64_t>(state[front+offset]) & support))return false; | |
| } | |
| return true; | |
| } | |
| State advance(const State& state,int64_t token)const override { | |
| if(!allowed(state,token))return {}; | |
| State result=state;int64_t front=covered(state); | |
| for(auto[offset,support]:(*patterns_)[token])result[front+offset]|=static_cast<int64_t>(support); | |
| return result; | |
| } | |
| }; | |
| class PlainGrammar:public Grammar { | |
| std::shared_ptr<const std::vector<int64_t>> lengths_,dims_; | |
| std::shared_ptr<const std::vector<bool>> phase_; | |
| public: | |
| PlainGrammar(int64_t steps,std::vector<int64_t> lengths,std::vector<int64_t> dims, | |
| py::array_t<bool,py::array::c_style|py::array::forcecast> phase) | |
| :lengths_(std::make_shared<const std::vector<int64_t>>(std::move(lengths))), | |
| dims_(std::make_shared<const std::vector<int64_t>>(std::move(dims))) { | |
| horizon=steps;vocabulary=lengths_->size(); | |
| if(steps<1 || dims_->empty() || phase.ndim()!=2 || phase.shape(0)!=static_cast<int64_t>(dims_->size()) || phase.shape(1)!=vocabulary) | |
| throw std::invalid_argument("invalid plain-BPE grammar metadata"); | |
| for(auto value:*lengths_)if(value<1)throw std::invalid_argument("invalid token expansion"); | |
| phase_=std::make_shared<const std::vector<bool>>(phase.data(),phase.data()+phase.size()); | |
| } | |
| std::shared_ptr<Grammar> clone(int64_t steps)const override { | |
| if(steps<1)throw std::invalid_argument("horizon must be positive"); | |
| auto result=std::make_shared<PlainGrammar>(*this);result->horizon=steps;return result; | |
| } | |
| State initial()const override{return {0};} | |
| int64_t covered(const State& state)const override { | |
| if(state.size()!=1 || state[0]<0)throw std::invalid_argument("invalid grammar state"); | |
| return state[0]/dims_->size(); | |
| } | |
| int64_t next_dimension(const State& state)const override{return (*dims_)[state[0]%dims_->size()];} | |
| bool allowed(const State& state,int64_t token)const override { | |
| return token>=0 && token<vocabulary && state[0]+(*lengths_)[token]<=horizon*static_cast<int64_t>(dims_->size()) && (*phase_)[(state[0]%dims_->size())*vocabulary+token]; | |
| } | |
| State advance(const State& state,int64_t token)const override { | |
| if(!allowed(state,token))return {}; | |
| return {state[0]+(*lengths_)[token]}; | |
| } | |
| }; | |
| using Grammars=std::vector<std::shared_ptr<Grammar>>; | |
| int64_t next_component(const Grammars& grammars,const States& states,bool time_major) { | |
| int64_t selected=grammars.size(),minimum=INT64_MAX; | |
| for(size_t i=0;i<grammars.size();++i) { | |
| const int64_t front=grammars[i]->covered(states[i]); | |
| if(front>=grammars[i]->horizon)continue; | |
| if(!time_major)return i; | |
| if(front<minimum){minimum=front;selected=i;} | |
| } | |
| return selected; | |
| } | |
| States initial(const Grammars& grammars) { | |
| States states;for(const auto& grammar:grammars)states.push_back(grammar->initial());return states; | |
| } | |
| bool advance(const Grammars& grammars,const std::vector<int64_t>& offsets,States& states,int64_t index,int64_t token) { | |
| if(index<0 || index>=static_cast<int64_t>(grammars.size()))return false; | |
| State next=grammars[index]->advance(states[index],token-offsets[index]); | |
| if(next.empty())return false; | |
| states[index]=std::move(next); | |
| return true; | |
| } | |
| py::tuple state_tuple(const State& state) { | |
| py::tuple result(state.size());for(size_t i=0;i<state.size();++i)result[i]=state[i];return result; | |
| } | |
| py::object route_advance(const Grammars& grammars,const std::vector<int64_t>& offsets, | |
| bool time_major,int64_t index,States states,int64_t token) { | |
| if(states.size()!=grammars.size() || offsets.size()!=grammars.size())throw std::invalid_argument("invalid routed metadata"); | |
| if(!advance(grammars,offsets,states,index,token))return py::none(); | |
| py::tuple local(states.size());for(size_t i=0;i<states.size();++i)local[i]=state_tuple(states[i]); | |
| return py::make_tuple(next_component(grammars,states,time_major),local); | |
| } | |
| std::vector<int64_t> interleave(const Grammars& grammars,const std::vector<int64_t>& offsets, | |
| bool time_major,const std::vector<std::vector<int64_t>>& streams) { | |
| if(streams.size()!=grammars.size() || offsets.size()!=grammars.size())throw std::invalid_argument("one stream per component is required"); | |
| States states=initial(grammars);std::vector<size_t> cursor(streams.size());std::vector<int64_t> output; | |
| int64_t index; | |
| while((index=next_component(grammars,states,time_major))<static_cast<int64_t>(grammars.size())) { | |
| if(cursor[index]==streams[index].size())throw std::invalid_argument("component stream ends before complete coverage"); | |
| const int64_t token=streams[index][cursor[index]++]+offsets[index]; | |
| if(!advance(grammars,offsets,states,index,token))throw std::invalid_argument("invalid routed component stream"); | |
| output.push_back(token); | |
| } | |
| for(size_t i=0;i<cursor.size();++i)if(cursor[i]!=streams[i].size())throw std::invalid_argument("extra component tokens"); | |
| return output; | |
| } | |
| std::vector<std::vector<int64_t>> split(const Grammars& grammars,const std::vector<int64_t>& offsets, | |
| bool time_major,const std::vector<int64_t>& tokens) { | |
| if(offsets.size()!=grammars.size())throw std::invalid_argument("invalid routed offsets"); | |
| States states=initial(grammars);std::vector<std::vector<int64_t>> output(grammars.size()); | |
| for(int64_t token:tokens) { | |
| int64_t index=next_component(grammars,states,time_major); | |
| if(!advance(grammars,offsets,states,index,token))throw std::invalid_argument("invalid routed token coverage"); | |
| output[index].push_back(token-offsets[index]); | |
| } | |
| if(next_component(grammars,states,time_major)!=static_cast<int64_t>(grammars.size())) | |
| throw std::invalid_argument("Incomplete action token stream"); | |
| return output; | |
| } | |
| using Streams=std::vector<std::vector<int64_t>>; | |
| using ComponentStreams=std::vector<Streams>; | |
| Streams interleave_batch(const Grammars& grammars,const std::vector<int64_t>& offsets, | |
| bool time_major,const ComponentStreams& streams) { | |
| if(streams.size()!=grammars.size() || streams.empty() || streams.front().empty()) | |
| throw std::invalid_argument("one non-empty batch per component is required"); | |
| const int64_t batch=streams.front().size(); | |
| for(const auto& rows:streams)if(static_cast<int64_t>(rows.size())!=batch) | |
| throw std::invalid_argument("component batches must have the same size"); | |
| Streams output(batch); | |
| py::gil_scoped_release release; | |
| ac2::parallel_for(batch,0,[&](int64_t b) { | |
| Streams local;for(const auto& rows:streams)local.push_back(rows[b]); | |
| output[b]=interleave(grammars,offsets,time_major,local); | |
| }); | |
| return output; | |
| } | |
| py::tuple decode_batch(const Grammars& prototypes,const std::vector<int64_t>& offsets, | |
| bool time_major,const py::iterable& streams, | |
| py::array_t<int64_t,py::array::c_style|py::array::forcecast> counts, | |
| int64_t cells_per_step) { | |
| if(prototypes.empty() || counts.ndim()!=1 || cells_per_step<1) | |
| throw std::invalid_argument("invalid routed batch metadata"); | |
| const auto rows=ac2::checked_token_streams(streams,counts.size(),true); | |
| int64_t steps=-1; | |
| ComponentStreams output(prototypes.size(),Streams(rows.size())); | |
| { | |
| py::gil_scoped_release release; | |
| for(const auto& row:rows) { | |
| int64_t cells=0; | |
| for(int64_t token:row) { | |
| if(counts.data()[token]<1)throw std::invalid_argument("token is outside the routed profile vocabularies"); | |
| cells+=counts.data()[token]; | |
| } | |
| if(cells%cells_per_step || cells/cells_per_step<1) | |
| throw std::invalid_argument("Incomplete action token stream: " + | |
| std::to_string(cells_per_step-cells%cells_per_step) + | |
| " more coordinate values are needed to finish a step. " | |
| "Constrain generation with codec.grammar(H, fps=...).next_token_mask(prefix) " | |
| "and check is_complete(tokens) for the intended H before decoding. " | |
| "Use codec.batch_decode(..., errors='return') to isolate invalid rows; " | |
| "do not pad truncated tokens with zeros."); | |
| if(steps>=0 && steps!=cells/cells_per_step) | |
| throw std::invalid_argument("all token streams must decode to the same length; use codec.batch_decode(...) for independent variable-length outputs"); | |
| steps=cells/cells_per_step; | |
| } | |
| Grammars grammars;for(const auto& prototype:prototypes)grammars.push_back(prototype->clone(steps)); | |
| ac2::parallel_for(rows.size(),0,[&](int64_t b) { | |
| auto local=split(grammars,offsets,time_major,rows[b]); | |
| for(size_t i=0;i<local.size();++i)output[i][b]=std::move(local[i]); | |
| }); | |
| } | |
| return py::make_tuple(steps,output); | |
| } | |
| std::vector<int64_t> complete(const Grammars& grammars,const std::vector<int64_t>& offsets, | |
| bool time_major,std::vector<int64_t> tokens,int64_t steps, | |
| const std::vector<std::vector<int64_t>>& zero_atoms) { | |
| if(grammars.empty() || offsets.size()!=grammars.size() || zero_atoms.size()!=grammars.size() || | |
| steps<1 || steps>grammars.front()->horizon)throw std::invalid_argument("invalid prefix completion metadata"); | |
| py::gil_scoped_release release; | |
| States states=initial(grammars); | |
| for(int64_t token:tokens) { | |
| int64_t index=next_component(grammars,states,time_major); | |
| if(!advance(grammars,offsets,states,index,token))throw std::invalid_argument("invalid routed token prefix"); | |
| } | |
| for(size_t i=0;i<grammars.size();++i)if(grammars[i]->covered(states[i])<steps) | |
| throw std::invalid_argument("action prefix does not cover the requested frames"); | |
| int64_t index; | |
| while((index=next_component(grammars,states,time_major))<static_cast<int64_t>(grammars.size())) { | |
| int64_t dimension=grammars[index]->next_dimension(states[index]); | |
| if(dimension<0 || dimension>=static_cast<int64_t>(zero_atoms[index].size())) | |
| throw std::invalid_argument("missing zero-motion primitive atom"); | |
| const int64_t token=offsets[index]+zero_atoms[index][dimension]; | |
| if(!advance(grammars,offsets,states,index,token))throw std::invalid_argument("invalid zero-motion suffix"); | |
| tokens.push_back(token); | |
| } | |
| return tokens; | |
| } | |
| } // namespace | |
| void register_grammar(py::module_& module) { | |
| py::class_<Grammar,std::shared_ptr<Grammar>>(module,"CoverageGrammar") | |
| .def("advance",[](const Grammar& g,const State& s,int64_t token)->py::object { | |
| State next=g.advance(s,token);return next.empty()?py::object(py::none()):py::object(state_tuple(next)); | |
| }) | |
| .def("mask",&Grammar::mask).def("covered",&Grammar::covered).def("clone",&Grammar::clone); | |
| py::class_<SetGrammar,Grammar,std::shared_ptr<SetGrammar>>(module,"SetGrammar") | |
| .def(py::init<int64_t,uint64_t,std::vector<int64_t>,std::vector<std::vector<std::pair<int64_t,uint64_t>>>>()); | |
| py::class_<PlainGrammar,Grammar,std::shared_ptr<PlainGrammar>>(module,"PlainGrammar") | |
| .def(py::init<int64_t,std::vector<int64_t>,std::vector<int64_t>,py::array_t<bool,py::array::c_style|py::array::forcecast>>()); | |
| module.def("route_advance",&route_advance); | |
| module.def("route_interleave",&interleave,py::call_guard<py::gil_scoped_release>()); | |
| module.def("route_split",&split,py::call_guard<py::gil_scoped_release>()); | |
| module.def("route_interleave_batch",&interleave_batch); | |
| module.def("route_decode_batch",&decode_batch); | |
| module.def("route_complete",&complete); | |
| } | |