diff --git a/ModNEF_Sources/modules/bias.vhd b/ModNEF_Sources/modules/bias.vhd deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/ModNEF_Sources/modules/neurons/BLIF/blif_parallel.vhd b/ModNEF_Sources/modules/neurons/BLIF/blif_parallel.vhd index 261ab777c14df5f2064313aaf7f02826909e069d..6564a366f137591f58d099f726cf2dbaec4f64bb 100644 --- a/ModNEF_Sources/modules/neurons/BLIF/blif_parallel.vhd +++ b/ModNEF_Sources/modules/neurons/BLIF/blif_parallel.vhd @@ -130,7 +130,7 @@ architecture Behavioral of BLif_Parallel is -- type definition type reception_state_t is (idle, request, get_data); - type transmission_state_t is (idle, voltage_update, check_arbitration, request, accept, wait_arbitration, arbitration_finish); + type transmission_state_t is (idle, voltage_update, check_arbitration, request, accept, wait_arbitration); -- ram signals signal data_read : std_logic_vector((output_neuron*weight_size)-1 downto 0); @@ -249,8 +249,9 @@ begin when check_arbitration => if spikes = no_spike then - transmission_state <= arbitration_finish; o_emu_busy <= '0'; + transmission_state <= idle; + tr_fsm_en := '0'; else transmission_state <= request; arb_spikes <= spikes; @@ -278,15 +279,12 @@ begin when wait_arbitration => start_arb <= '0'; if arb_busy = '0' then - transmission_state <= arbitration_finish; + transmission_state <= idle; + o_emu_busy <= '0'; + tr_fsm_en := '0'; else transmission_state <= wait_arbitration; end if; - - when arbitration_finish => - transmission_state <= idle; - o_emu_busy <= '0'; - tr_fsm_en := '0'; end case; end if; end if; diff --git a/ModNEF_Sources/modules/neurons/BLIF/blif_sequential.vhd b/ModNEF_Sources/modules/neurons/BLIF/blif_sequential.vhd index 00dd851f1bd502d223f72fb5fc0f16b827974522..6cc760a32b972fb134be7b245c68ab851d931694 100644 --- a/ModNEF_Sources/modules/neurons/BLIF/blif_sequential.vhd +++ b/ModNEF_Sources/modules/neurons/BLIF/blif_sequential.vhd @@ -88,7 +88,7 @@ architecture Behavioral of BLif_Sequential is -- type definition type array_t is array(output_neuron-1 downto 0) of std_logic_vector(variable_size-1 downto 0); type transmission_state_t is (idle, request, accept, get_voltage, emulate, set_voltage, emulate_finish); - type reception_state_t is (idle, request, wait_data, get_data, update_current); + type reception_state_t is (idle, request, get_data); -- ram signals signal data_read : std_logic_vector((output_neuron*weight_size)-1 downto 0); @@ -151,13 +151,6 @@ begin reception_state <= request; end if; - when wait_data => - if i_emu_busy = '1' then - reception_state <= get_data; - else - reception_state <= wait_data; - end if; - when get_data => spike_flag <= i_spike_flag; if i_emu_busy='0' and spike_flag = '0' then @@ -169,10 +162,6 @@ begin current_en <= '1'; reception_state <= get_data; end if; - - when update_current => - reception_state <= idle; - rec_fsm_en := '0'; end case; end if; end if; @@ -256,7 +245,6 @@ begin when accept => if i_ack <= '0' then transmission_state <= get_voltage; - o_emu_busy <= '1'; else transmission_state <= accept; o_req <= '0'; diff --git a/ModNEF_Sources/modules/neurons/BLIF/rblif_parallel.vhd b/ModNEF_Sources/modules/neurons/BLIF/rblif_parallel.vhd index 16f9528ab45ea3f75c77da2d565725076ed70007..4fdb9c13587989385207042fa7b9404062d3e456 100644 --- a/ModNEF_Sources/modules/neurons/BLIF/rblif_parallel.vhd +++ b/ModNEF_Sources/modules/neurons/BLIF/rblif_parallel.vhd @@ -7,7 +7,7 @@ -- Authors : Aurelie Saulquin -- Email : aurelie.saulquin@univ-lille.fr -- --- Version : 1.1.0 +-- Version : 1.1.1 -- Version comment : stable version -- -- Licenses : cern-ohl-s-2.0 @@ -251,7 +251,6 @@ begin if i_start_emu = '1' then tr_fsm_en := '1'; - transmission_neuron_en <= '1'; end if; if rising_edge(i_clk) then @@ -260,8 +259,8 @@ begin start_calc <= '0'; o_emu_busy <= '0'; o_req <= '0'; - rec_ram_en <= '1'; - rec_neuron_en <= '1'; + rec_ram_en <= '0'; + rec_neuron_en <= '0'; rec_spike_flag <= '0'; else case transmission_state is @@ -283,7 +282,7 @@ begin end if; when voltage_update => - transmission_neuron_en <= '0'; + transmission_neuron_en <= '1'; start_calc <= '0'; transmission_state <= check_arbitration; @@ -311,6 +310,7 @@ begin transmission_state <= wait_arbitration; start_arb <= '1'; rec_ram_en <= '1'; + rec_neuron_en <= '1'; rec_spike_flag <= arb_spike_flag; else @@ -331,6 +331,7 @@ begin transmission_state <= idle; o_emu_busy <= '0'; rec_neuron_en <= '0'; + rec_ram_en <= '0'; tr_fsm_en := '0'; end case; end if; diff --git a/ModNEF_Sources/modules/neurons/BLIF/rblif_sequential.vhd b/ModNEF_Sources/modules/neurons/BLIF/rblif_sequential.vhd index 11b43b5285e66126a4b6477f85ebcb8862569a1f..745760a8d81f52b31415b706c15b616f02e1a734 100644 --- a/ModNEF_Sources/modules/neurons/BLIF/rblif_sequential.vhd +++ b/ModNEF_Sources/modules/neurons/BLIF/rblif_sequential.vhd @@ -88,7 +88,7 @@ architecture Behavioral of RBLif_Sequential is -- type definition type array_t is array(output_neuron-1 downto 0) of std_logic_vector(variable_size-1 downto 0); - type reception_state_t is (idle, request, wait_data, get_data, update_current); + type reception_state_t is (idle, request, get_data); type transmission_state_t is (idle, request, accept, get_voltage, emulate, set_voltage, emulate_finish); -- output signals @@ -161,13 +161,6 @@ begin reception_state <= request; end if; - when wait_data => - if i_emu_busy = '1' then - reception_state <= get_data; - else - reception_state <= wait_data; - end if; - when get_data => spike_flag <= i_spike_flag; if i_emu_busy='0' and spike_flag = '0' then @@ -179,10 +172,6 @@ begin current_en <= '1'; reception_state <= get_data; end if; - - when update_current => - reception_state <= idle; - rec_fsm_en := '0'; end case; end if; end if; @@ -305,7 +294,6 @@ begin when accept => if i_ack <= '0' then transmission_state <= get_voltage; - o_emu_busy <= '1'; rec_ram_en <= '1'; rec_current_en <= '1'; else diff --git a/ModNEF_Sources/modules/neurons/SLIF/rslif_parallel.vhd b/ModNEF_Sources/modules/neurons/SLIF/rslif_parallel.vhd index 4e181835525b78a2f39c9cf9fed7fa1dc8c5b3d8..894bed2072a5453db321baf8ad6d1490976d80c1 100644 --- a/ModNEF_Sources/modules/neurons/SLIF/rslif_parallel.vhd +++ b/ModNEF_Sources/modules/neurons/SLIF/rslif_parallel.vhd @@ -7,7 +7,7 @@ -- Authors : Aurelie Saulquin -- Email : aurelie.saulquin@univ-lille.fr -- --- Version : 1.1.0 +-- Version : 1.1.1 -- Version comment : stable version -- -- Licenses : cern-ohl-s-2.0 @@ -240,8 +240,8 @@ begin start_calc <= '0'; o_emu_busy <= '0'; o_req <= '0'; - rec_ram_en <= '1'; - rec_neuron_en <= '1'; + rec_ram_en <= '0'; + rec_neuron_en <= '0'; rec_spike_flag <= '0'; else case transmission_state is @@ -263,7 +263,7 @@ begin end if; when voltage_update => - transmission_neuron_en <= '0'; + transmission_neuron_en <= '1'; start_calc <= '0'; transmission_state <= check_arbitration; @@ -291,6 +291,7 @@ begin transmission_state <= wait_arbitration; start_arb <= '1'; rec_ram_en <= '1'; + rec_neuron_en <= '1'; rec_spike_flag <= arb_spike_flag; else @@ -311,6 +312,7 @@ begin transmission_state <= idle; o_emu_busy <= '0'; rec_neuron_en <= '0'; + rec_ram_en <= '0'; tr_fsm_en := '0'; end case; end if; @@ -338,7 +340,7 @@ begin mem_init_file => mem_init_file_rec ) port map ( i_clk => i_clk, - i_en => '1', + i_en => rec_ram_en, i_addr => output_aer, o_data => rec_data_read ); diff --git a/ModNEF_Sources/modules/neurons/SLIF/rslif_sequential.vhd b/ModNEF_Sources/modules/neurons/SLIF/rslif_sequential.vhd index d975f60b74bf2c96d96b90b0badec180b1358667..cc01a68a48500e5cf7503511c0ee54662336fb81 100644 --- a/ModNEF_Sources/modules/neurons/SLIF/rslif_sequential.vhd +++ b/ModNEF_Sources/modules/neurons/SLIF/rslif_sequential.vhd @@ -88,7 +88,7 @@ architecture Behavioral of RSLif_Sequential is -- type definition type array_t is array(output_neuron-1 downto 0) of std_logic_vector(variable_size-1 downto 0); - type reception_state_t is (idle, request, wait_data, get_data, update_current); + type reception_state_t is (idle, request, get_data); type transmission_state_t is (idle, request, accept, get_voltage, emulate, set_voltage, emulate_finish); -- output signals @@ -161,13 +161,6 @@ begin reception_state <= request; end if; - when wait_data => - if i_emu_busy = '1' then - reception_state <= get_data; - else - reception_state <= wait_data; - end if; - when get_data => spike_flag <= i_spike_flag; if i_emu_busy='0' and spike_flag = '0' then @@ -179,10 +172,6 @@ begin current_en <= '1'; reception_state <= get_data; end if; - - when update_current => - reception_state <= idle; - rec_fsm_en := '0'; end case; end if; end if; @@ -305,7 +294,6 @@ begin when accept => if i_ack <= '0' then transmission_state <= get_voltage; - o_emu_busy <= '1'; rec_ram_en <= '1'; rec_current_en <= '1'; else diff --git a/ModNEF_Sources/modules/neurons/SLIF/simplified_lif.vhd b/ModNEF_Sources/modules/neurons/SLIF/simplified_lif.vhd index 892bcdb717be626b5addded26c3f64f4791eba9c..793a13bfd95c3740f58a3a44a11cc8b728d4e1b5 100644 --- a/ModNEF_Sources/modules/neurons/SLIF/simplified_lif.vhd +++ b/ModNEF_Sources/modules/neurons/SLIF/simplified_lif.vhd @@ -7,7 +7,7 @@ -- Authors : Aurelie Saulquin -- Email : aurelie.saulquin@univ-lille.fr -- --- Version : 1.2.0 +-- Version : 1.3.0 -- Version comment : stable version -- -- Licenses : cern-ohl-s-2.0 @@ -70,6 +70,8 @@ begin o_spike <= spike; process(i_clk, i_inc_I, i_calc, i_en) + variable I : std_logic_vector(weight_size-1 downto 0); + variable I_rec : std_logic_vector(weight_size-1 downto 0); begin if rising_edge(i_clk) then if i_reset = '1' then @@ -78,14 +80,21 @@ begin if i_en = '1' then if weight_signed then - if spike_flag = '1' or spike_flag_rec = '1' then - if spike_flag = '1' and spike_flag_rec = '0' then - V <= std_logic_vector(signed(V)+signed(weight)); - elsif spike_flag = '0' and spike_flag_rec = '1' then - V <= std_logic_vector(signed(V)+signed(weight_rec)); + if i_inc_I = '1' or i_inc_I_rec = '1' then + + if i_inc_I = '1' then + I := std_logic_vector(signed(i_w)); else - V <= std_logic_vector(signed(V)+signed(weight)+signed(weight_rec)); - end if; + I := (others=>'0'); + end if; + + if i_inc_I_rec = '1' then + I_rec := std_logic_vector(signed(i_w_rec)); + else + I_rec := (others=>'0'); + end if; + + V <= std_logic_vector(signed(V) + signed(I) + signed(I_rec)); elsif i_calc = '1' then if signed(V) >= signed(v_threshold+v_leak) then spike <= '1'; @@ -99,15 +108,24 @@ begin end if; end if; else - if spike_flag = '1' or spike_flag_rec = '1' then - if spike_flag = '1' and spike_flag_rec = '0' then - V <= std_logic_vector(unsigned(V)+unsigned(weight)); - elsif spike_flag = '0' and spike_flag_rec = '1' then - V <= std_logic_vector(unsigned(V)+unsigned(weight_rec)); - else - V <= std_logic_vector(unsigned(V)+unsigned(weight)+unsigned(weight_rec)); - end if; - elsif i_calc = '1' then + if i_inc_I = '1' or i_inc_I_rec = '1' then + + if i_inc_I = '1' then + I := std_logic_vector(unsigned(i_w)); + else + I := (others=>'0'); + end if; + + if i_inc_I_rec = '1' then + I_rec := std_logic_vector(unsigned(i_w_rec)); + else + I_rec := (others=>'0'); + end if; + + V <= std_logic_vector(unsigned(V) + unsigned(I) + unsigned(I_rec)); + + elsif i_calc = '1' then + if unsigned(V) >= unsigned(v_threshold+v_leak) then spike <= '1'; V <= V_rest; @@ -121,10 +139,10 @@ begin end if; end if; - spike_flag <= i_inc_I; - weight <= i_w; - spike_flag_rec <= i_inc_I_rec; - weight_rec <= i_w_rec; + -- spike_flag <= i_inc_I; + -- weight <= i_w; + -- spike_flag_rec <= i_inc_I_rec; + -- weight_rec <= i_w_rec; end if; end if; diff --git a/ModNEF_Sources/modules/neurons/SLIF/slif_parallel.vhd b/ModNEF_Sources/modules/neurons/SLIF/slif_parallel.vhd index 5a50090fb96cc39e42ff9e7d7a7eed3466601172..8c4b605ffa4f26a0b6f51955599f9ad9414c04ad 100644 --- a/ModNEF_Sources/modules/neurons/SLIF/slif_parallel.vhd +++ b/ModNEF_Sources/modules/neurons/SLIF/slif_parallel.vhd @@ -131,7 +131,7 @@ architecture Behavioral of SLif_Parallel is -- type definition type reception_state_t is (idle, request, get_data); - type transmission_state_t is (idle, voltage_update, check_arbitration, request, accept, wait_arbitration, arbitration_finish); + type transmission_state_t is (idle, voltage_update, check_arbitration, request, accept, wait_arbitration); -- ram signals signal data_read : std_logic_vector((output_neuron*weight_size)-1 downto 0); @@ -253,8 +253,9 @@ begin when check_arbitration => if spikes = no_spike then - transmission_state <= arbitration_finish; + transmission_state <= idle; o_emu_busy <= '0'; + tr_fsm_en := '0'; else transmission_state <= request; arb_spikes <= spikes; @@ -281,15 +282,12 @@ begin when wait_arbitration => start_arb <= '0'; if arb_busy = '0' then - transmission_state <= arbitration_finish; + transmission_state <= idle; + o_emu_busy <= '0'; + tr_fsm_en := '0'; else transmission_state <= wait_arbitration; end if; - - when arbitration_finish => - transmission_state <= idle; - o_emu_busy <= '0'; - tr_fsm_en := '0'; end case; end if; end if; diff --git a/ModNEF_Sources/modules/neurons/SLIF/slif_sequential.vhd b/ModNEF_Sources/modules/neurons/SLIF/slif_sequential.vhd index 391eaa6fb463493dfd5be29910427856ac309f98..362d9829eab1ec718b882841ff1ebc0af2cbcfa3 100644 --- a/ModNEF_Sources/modules/neurons/SLIF/slif_sequential.vhd +++ b/ModNEF_Sources/modules/neurons/SLIF/slif_sequential.vhd @@ -89,7 +89,7 @@ architecture Behavioral of SLif_Sequential is -- type definition type array_t is array(output_neuron-1 downto 0) of std_logic_vector(variable_size-1 downto 0); type transmission_state_t is (idle, request, accept, get_voltage, emulate, set_voltage, emulate_finish); - type reception_state_t is (idle, request, wait_data, get_data, update_current); + type reception_state_t is (idle, request, get_data); -- ram signals signal data_read : std_logic_vector((output_neuron*weight_size)-1 downto 0); @@ -149,13 +149,6 @@ begin reception_state <= request; end if; - when wait_data => - if i_emu_busy = '1' then - reception_state <= get_data; - else - reception_state <= wait_data; - end if; - when get_data => spike_flag <= i_spike_flag; if i_emu_busy='0' and spike_flag = '0' then @@ -167,10 +160,6 @@ begin current_en <= '1'; reception_state <= get_data; end if; - - when update_current => - reception_state <= idle; - rec_fsm_en := '0'; end case; end if; end if; @@ -259,7 +248,6 @@ begin when accept => if i_ack <= '0' then transmission_state <= get_voltage; - o_emu_busy <= '1'; else transmission_state <= accept; o_req <= '0'; diff --git a/ModNEF_Sources/modules/neurons/ShiftLif/rshiftlif_parallel.vhd b/ModNEF_Sources/modules/neurons/ShiftLif/rshiftlif_parallel.vhd index 1af3b8a8e0c779c114cdb0e08196d8d2ecdf3323..8d3d6adc1cbdc7b3ce296e84588644fca8e5e398 100644 --- a/ModNEF_Sources/modules/neurons/ShiftLif/rshiftlif_parallel.vhd +++ b/ModNEF_Sources/modules/neurons/ShiftLif/rshiftlif_parallel.vhd @@ -7,7 +7,7 @@ -- Authors : Aurelie Saulquin -- Email : aurelie.saulquin@univ-lille.fr -- --- Version : 1.1.0 +-- Version : 1.1.1 -- Version comment : stable version -- -- Licenses : cern-ohl-s-2.0 @@ -237,8 +237,8 @@ begin start_calc <= '0'; o_emu_busy <= '0'; o_req <= '0'; - rec_ram_en <= '1'; - rec_neuron_en <= '1'; + rec_ram_en <= '0'; + rec_neuron_en <= '0'; rec_spike_flag <= '0'; else case transmission_state is @@ -260,7 +260,7 @@ begin end if; when voltage_update => - transmission_neuron_en <= '0'; + transmission_neuron_en <= '1'; start_calc <= '0'; transmission_state <= check_arbitration; @@ -288,6 +288,7 @@ begin transmission_state <= wait_arbitration; start_arb <= '1'; rec_ram_en <= '1'; + rec_neuron_en <= '1'; rec_spike_flag <= arb_spike_flag; else @@ -308,6 +309,7 @@ begin transmission_state <= idle; o_emu_busy <= '0'; rec_neuron_en <= '0'; + rec_ram_en <= '0'; tr_fsm_en := '0'; end case; end if; @@ -335,7 +337,7 @@ begin mem_init_file => mem_init_file_rec ) port map ( i_clk => i_clk, - i_en => '1', + i_en => rec_ram_en, i_addr => output_aer, o_data => rec_data_read ); diff --git a/ModNEF_Sources/modules/neurons/ShiftLif/rshiftlif_sequential.vhd b/ModNEF_Sources/modules/neurons/ShiftLif/rshiftlif_sequential.vhd index fa3c0cb267fb8f7124fa9c6932ea4135e6cbc1b1..9854bca3ccc5d7d3abca4c02f7abbabce6066530 100644 --- a/ModNEF_Sources/modules/neurons/ShiftLif/rshiftlif_sequential.vhd +++ b/ModNEF_Sources/modules/neurons/ShiftLif/rshiftlif_sequential.vhd @@ -85,7 +85,7 @@ architecture Behavioral of RShiftLif_Sequential is -- type definition type array_t is array(output_neuron-1 downto 0) of std_logic_vector(variable_size-1 downto 0); - type reception_state_t is (idle, request, wait_data, get_data, update_current); + type reception_state_t is (idle, request, get_data); type transmission_state_t is (idle, request, accept, get_voltage, emulate, set_voltage, emulate_finish); -- output signals @@ -158,13 +158,6 @@ begin reception_state <= request; end if; - when wait_data => - if i_emu_busy = '1' then - reception_state <= get_data; - else - reception_state <= wait_data; - end if; - when get_data => spike_flag <= i_spike_flag; if i_emu_busy='0' and spike_flag = '0' then @@ -176,10 +169,6 @@ begin current_en <= '1'; reception_state <= get_data; end if; - - when update_current => - reception_state <= idle; - rec_fsm_en := '0'; end case; end if; end if; @@ -302,7 +291,6 @@ begin when accept => if i_ack <= '0' then transmission_state <= get_voltage; - o_emu_busy <= '1'; rec_ram_en <= '1'; rec_current_en <= '1'; else diff --git a/ModNEF_Sources/modules/neurons/ShiftLif/shift_lif.vhd b/ModNEF_Sources/modules/neurons/ShiftLif/shift_lif.vhd index 84e97fa113973069f2746b271a066ee365640a92..9f6b46094e4d795df63e85559d716f5a1980e758 100644 --- a/ModNEF_Sources/modules/neurons/ShiftLif/shift_lif.vhd +++ b/ModNEF_Sources/modules/neurons/ShiftLif/shift_lif.vhd @@ -7,7 +7,7 @@ -- Authors : Aurelie Saulquin -- Email : aurelie.saulquin@univ-lille.fr -- --- Version : 1.1.0 +-- Version : 1.2.0 -- Version comment : stable version -- -- Licenses : cern-ohl-s-2.0 @@ -69,6 +69,9 @@ begin o_spike <= spike; process(i_clk, i_inc_I, i_calc, i_en) + variable I : std_logic_vector(weight_size-1 downto 0); + variable I_rec : std_logic_vector(weight_size-1 downto 0); + variable v_buff : std_logic_vector(variable_size-1 downto 0); begin if rising_edge(i_clk) then if i_reset = '1' then @@ -77,46 +80,62 @@ begin if i_en = '1' then if weight_signed then - if spike_flag = '1' or spike_flag_rec = '1' then - if spike_flag = '1' and spike_flag_rec = '0' then - V <= std_logic_vector(signed(V) + signed(weight)); - elsif spike_flag = '0' and spike_flag_rec = '1' then - V <= std_logic_vector(signed(V) + signed(weight_rec)); + if i_inc_I = '1' or i_inc_I_rec = '1' then + + if i_inc_I = '1' then + I := std_logic_vector(signed(i_w)); else - V <= std_logic_vector(signed(V) + signed(weight) + signed(weight_rec)); + I := (others=>'0'); end if; + + if i_inc_I_rec = '1' then + I_rec := std_logic_vector(signed(i_w_rec)); + else + I_rec := (others=>'0'); + end if; + + V <= std_logic_vector(signed(V) + signed(I) + signed(I_rec)); elsif i_calc='1' then - if signed(V) >= signed(v_threshold) then + V_buff := std_logic_vector(signed(V)-signed(shift_right(signed(V), shift))); + if signed(V_buff) >= signed(v_threshold) then spike <= '1'; if reset = "zero" then V <= (others=>'0'); else - V <= std_logic_vector(signed(V) - signed(v_threshold)); + V <= std_logic_vector(signed(V_buff) - signed(v_threshold)); end if; else - V <= std_logic_vector(signed(V)-signed(shift_right(signed(V), shift))); + V <= V_buff; spike <= '0'; end if; end if; else - if spike_flag = '1' or spike_flag_rec = '1' then - if spike_flag = '1' and spike_flag_rec = '0' then - V <= std_logic_vector(unsigned(V) + unsigned(weight)); - elsif spike_flag = '0' and spike_flag_rec = '1' then - V <= std_logic_vector(unsigned(V) + unsigned(weight_rec)); + if i_inc_I = '1' or i_inc_I_rec = '1' then + + if i_inc_I = '1' then + I := std_logic_vector(unsigned(i_w)); else - V <= std_logic_vector(unsigned(V) + unsigned(weight) + unsigned(weight_rec)); + I := (others=>'0'); end if; + + if i_inc_I_rec = '1' then + I_rec := std_logic_vector(unsigned(i_w_rec)); + else + I_rec := (others=>'0'); + end if; + + V <= std_logic_vector(unsigned(V) + unsigned(I) + unsigned(I_rec)); elsif i_calc='1' then - if unsigned(V) >= unsigned(v_threshold) then + V_buff := std_logic_vector(unsigned(V)-unsigned(shift_right(unsigned(V), shift))); + if unsigned(V_buff) >= unsigned(v_threshold) then spike <= '1'; if reset = "zero" then V <= (others=>'0'); else - V <= std_logic_vector(unsigned(V) - unsigned(v_threshold)); + V <= std_logic_vector(unsigned(V_buff) - unsigned(v_threshold)); end if; else - V <= std_logic_vector(unsigned(V)-unsigned(shift_right(unsigned(V), shift))); + V <= V_buff; spike <= '0'; end if; end if; diff --git a/ModNEF_Sources/modules/neurons/ShiftLif/shiftlif_parallel.vhd b/ModNEF_Sources/modules/neurons/ShiftLif/shiftlif_parallel.vhd index f03d9e803f6075ec6b5d67ec9ba78260e68f7720..32a3aca72754c8862b01753e55ee7938935498a4 100644 --- a/ModNEF_Sources/modules/neurons/ShiftLif/shiftlif_parallel.vhd +++ b/ModNEF_Sources/modules/neurons/ShiftLif/shiftlif_parallel.vhd @@ -339,3 +339,4 @@ begin end generate neuron_generation; end Behavioral; + diff --git a/ModNEF_Sources/modules/neurons/ShiftLif/shiftlif_sequential.vhd b/ModNEF_Sources/modules/neurons/ShiftLif/shiftlif_sequential.vhd index b86bb30c4323e616681da8a1b8f5900ef8a1f751..0533fdc7e7f1b63ce0966a51cea9b063cc8c4219 100644 --- a/ModNEF_Sources/modules/neurons/ShiftLif/shiftlif_sequential.vhd +++ b/ModNEF_Sources/modules/neurons/ShiftLif/shiftlif_sequential.vhd @@ -85,7 +85,7 @@ architecture Behavioral of ShiftLif_Sequential is -- type definition type array_t is array(output_neuron-1 downto 0) of std_logic_vector(variable_size-1 downto 0); - type reception_state_t is (idle, request, wait_data, get_data, update_current); + type reception_state_t is (idle, request, get_data); type transmission_state_t is (idle, request, accept, get_voltage, emulate, set_voltage, emulate_finish); -- ram signals @@ -146,13 +146,6 @@ begin reception_state <= request; end if; - when wait_data => - if i_emu_busy = '1' then - reception_state <= get_data; - else - reception_state <= wait_data; - end if; - when get_data => spike_flag <= i_spike_flag; if i_emu_busy='0' and spike_flag = '0' then @@ -164,10 +157,6 @@ begin current_en <= '1'; reception_state <= get_data; end if; - - when update_current => - reception_state <= idle; - rec_fsm_en := '0'; end case; end if; end if; @@ -252,7 +241,6 @@ begin when accept => if i_ack <= '0' then transmission_state <= get_voltage; - o_emu_busy <= '1'; else transmission_state <= accept; o_req <= '0'; diff --git a/ModNEF_Sources/modules/uart/uart_1step.vhd b/ModNEF_Sources/modules/uart/uart_1step.vhd deleted file mode 100644 index 50ecbe828baaa98cfa3d61176d11c04d9a3b0244..0000000000000000000000000000000000000000 --- a/ModNEF_Sources/modules/uart/uart_1step.vhd +++ /dev/null @@ -1,390 +0,0 @@ ----------------------------------------------------------------------------------- --- --- Project : ModNEF --- Component name : uart_1step --- Depencies : uart_controller --- --- Authors : Aurelie Saulquin --- Email : aurelie.saulquin@univ-lille.fr --- --- Version : 1.0 --- Version comment : stable version --- --- Licenses : cern-ohl-s-2.0 --- --- Description : --- UART component where one data transmission is use for one emulation step --- Component will receive data, send all data to network and receive data --- from network and transmit it to computer --- ----------------------------------------------------------------------------------- - -library IEEE; -use IEEE.std_logic_1164.all; -use work.math.all; - -entity uart_1Step is - generic( - clk_freq : integer := 100_000_000; - baud_rate : integer := 115_200; - - queue_read_depth : integer := 32; - queue_read_type : string := "fifo"; - - queue_write_type : string := "fifo"; - - input_layer_size : integer := 8; - output_layer_size : integer := 8 - ); - port ( - i_clk : in std_logic; - i_en : in std_logic; - - i_tx : in std_logic; - o_rx : out std_logic; - - i_emu_ready : in std_logic; - o_start_emu : out std_logic; - o_reset_membrane : out std_logic; - - i_req : in std_logic; - o_ack : out std_logic; - i_emu_busy : in std_logic; - i_spike_flag : in std_logic; - i_aer : in std_logic_vector(log_b(output_layer_size, 2)-1 downto 0); - - o_req : out std_logic; - i_ack : in std_logic; - o_emu_busy : out std_logic; - o_spike_flag : out std_logic; - o_aer : out std_logic_vector(log_b(input_layer_size, 2)-1 downto 0) - ); -end uart_1Step; - -architecture Behavioral of uart_1Step is - - component uart_controller is - generic( - clk_freq : integer := 100_000_000; - baud_rate : integer := 115_200; - oversamp_rate : integer := 16; - - queue_read_depth : integer := 64; - queue_read_width : integer := 1; - queue_read_type : string := "fifo"; - - queue_write_depth : integer := 16; - queue_write_width : integer := 1; - queue_write_type : string := "fifo" -- fifo or lifo - ); - port( - i_clk : in std_logic; - i_en : in std_logic; - o_busy : out std_logic; - o_reset_detected : out std_logic; - - -- UART pins - i_tx : in std_logic; - o_rx : out std_logic; - o_uart_busy : out std_logic; - - -- read I/O - o_read_data : out std_logic_vector(queue_read_width*8-1 downto 0); - i_read_pop : in std_logic; - o_read_busy : out std_logic; - o_read_queue_empty : out std_logic; - o_read_queue_full : out std_logic; - - -- write I/O - i_start_transmission : in std_logic; - i_write_data : in std_logic_vector(queue_write_width*8-1 downto 0); - i_write_push : in std_logic; - o_write_busy : out std_logic; - o_write_queue_empty : out std_logic; - o_write_queue_full : out std_logic - - ); - end component; - - -- type definition - type emu_state_t is (idle, wait_data, check_emu, emulate, wait_out_aer, send_aer, wait_transmission); - type uart_to_network_state_t is (idle, check_emu, request, accept, transfert); - type network_to_uart_state_t is (idle, wait_request, accept, wait_aer); - - -- queue constant definition - --constant queue_read_depth : integer := 255; - constant queue_read_width : integer := log_b(queue_read_depth, 256); - - constant queue_write_depth : integer := output_layer_size; - constant queue_write_width : integer := log_b(output_layer_size, 256); - - -- read queue signals - signal read_data : std_logic_vector(queue_read_width*8-1 downto 0) := (others=>'0'); - signal read_pop : std_logic := '0'; - signal read_busy : std_logic; - signal read_empty : std_logic; - - -- write queue signals - signal write_data : std_logic_vector(queue_write_width*8-1 downto 0) := (others=>'0'); - signal write_push : std_logic := '0'; - signal write_busy : std_logic; - - -- uart signals - signal start_uart_transmission : std_logic := '0'; - - -- emulation signals - signal emu_state : emu_state_t := idle; - signal start_emu : std_logic; - - -- membrane reset signals - signal reset_detected : std_logic := '0'; - signal reset_membrane : std_logic := '0'; - - -- uart to network signals - signal uart_to_network_state : uart_to_network_state_t := idle; - signal uart_to_network_busy : std_logic := '0'; - - -- network to uart signals - signal network_to_uart_state : network_to_uart_state_t := idle; - signal network_to_uart_busy : std_logic := '0'; - -begin - - o_start_emu <= start_emu; - - o_reset_membrane <= reset_membrane; - - -- controller FSM - process(i_clk, i_en) - begin - if rising_edge(i_clk) then - if i_en = '0' then - emu_state <= idle; - start_emu <= '0'; - start_uart_transmission <= '0'; - else - case emu_state is - when idle => - start_emu <= '0'; - start_uart_transmission <= '0'; - reset_membrane <= '0'; - if read_busy = '1' then - emu_state <= wait_data; - else - emu_state <= idle; - end if; - - when wait_data => - if read_busy = '0' then - emu_state <= check_emu; - else - emu_state <= wait_data; - end if; - - when check_emu => - if i_emu_ready = '1' then - emu_state <= emulate; - start_emu <= '1'; - else - emu_state <= check_emu; - end if; - - when emulate => - start_emu <= '0'; - if network_to_uart_busy = '1' then - emu_state <= wait_out_aer; - else - emu_state <= emulate; - end if; - - when wait_out_aer => - if i_emu_ready = '1' then - emu_state <= send_aer; - start_uart_transmission <= '1'; - reset_membrane <= reset_detected; - else - emu_state <= wait_out_aer; - end if; - - when send_aer => - start_uart_transmission <= '0'; - reset_membrane <= '0'; - if write_busy = '1' then - emu_state <= wait_transmission; - else - emu_state <= send_aer; - end if; - - when wait_transmission => - if write_busy = '0' then - emu_state <= idle; - else - emu_state <= wait_transmission; - end if; - end case; - end if; - end if; - end process; - - -- Controller to network FSM - o_aer <= read_data(log_b(input_layer_size, 2)-1 downto 0) when uart_to_network_state = transfert else (others=>'0'); - process(i_clk, i_en) - begin - if rising_edge(i_clk) then - if i_en = '0' then - o_req <= '0'; - o_spike_flag <= '0'; - o_emu_busy <= '0'; - read_pop <= '0'; - uart_to_network_busy <= '0'; - uart_to_network_state <= idle; - else - case uart_to_network_state is - when idle => - o_req <= '0'; - o_spike_flag <= '0'; - read_pop <= '0'; - if start_emu = '1' then - uart_to_network_state <= check_emu; - o_emu_busy <= '1'; - uart_to_network_busy <= '1'; - else - uart_to_network_state <= idle; - o_emu_busy <= '0'; - uart_to_network_busy <= '0'; - end if; - - when check_emu => - if read_empty = '1' then - uart_to_network_state <= idle; - o_emu_busy <= '0'; - uart_to_network_busy <= '0'; - else - uart_to_network_state <= request; - o_req <= '1'; - end if; - - when request => - if i_ack = '1' then - uart_to_network_state <= accept; - o_req <= '0'; - else - uart_to_network_state <= request; - o_req <= '1'; - end if; - - when accept => - if i_ack = '0' then - uart_to_network_state <= transfert; - read_pop <= '1'; - else - uart_to_network_state <= accept; - end if; - - when transfert => - if read_empty = '1' then - uart_to_network_state <= idle; - o_emu_busy <= '0'; - read_pop <= '0'; - o_spike_flag <= '0'; - uart_to_network_busy <= '0'; - else - uart_to_network_state <= transfert; - read_pop <= '1'; - o_spike_flag <= '1'; - end if; - end case; - end if; - end if; - end process; - - - write_data(log_b(output_layer_size, 2)-1 downto 0) <= i_aer when network_to_uart_state = wait_aer else (others=>'0'); - write_push <= i_spike_flag when network_to_uart_state = wait_aer else '0'; - - -- Network to Controller FSM - process(i_clk, i_en) - begin - if i_en = '0' then - network_to_uart_state <= idle; - network_to_uart_busy <= '0'; - o_ack <= '0'; - else - if rising_edge(i_clk) then - case network_to_uart_state is - when idle => - o_ack <= '0'; - if i_emu_busy = '1' then - network_to_uart_state <= wait_request; - network_to_uart_busy <= '1'; - else - network_to_uart_state <= idle; - network_to_uart_busy <= '0'; - end if; - - when wait_request => - if i_emu_busy = '0' then - network_to_uart_state <= idle; - network_to_uart_busy <= '0'; - elsif i_req = '1' then - o_ack <= '1'; - network_to_uart_state <= accept; - else - network_to_uart_state <= wait_request; - end if; - - when accept => - if i_req = '0' then - network_to_uart_state <= wait_aer; - o_ack <= '0'; - else - network_to_uart_state <= accept; - o_ack <= '1'; - end if; - - when wait_aer => - if i_emu_busy = '0' then - network_to_uart_state <= idle; - network_to_uart_busy <= '0'; - else - network_to_uart_state <= wait_aer; - end if; - end case; - end if; - end if; - end process; - - c_uart_controller : uart_controller generic map( - clk_freq => clk_freq, - baud_rate => baud_rate, - oversamp_rate => 16, - - queue_read_depth => queue_read_depth, - queue_read_width => queue_read_width, - queue_read_type => queue_read_type, - - queue_write_depth => queue_write_depth, - queue_write_width => queue_write_width, - queue_write_type => queue_write_type - ) port map( - i_clk => i_clk, - i_en => i_en, - o_busy => open, - o_reset_detected => reset_detected, - i_tx => i_tx, - o_rx => o_rx, - o_uart_busy => open, - o_read_data => read_data, - i_read_pop => read_pop, - o_read_busy => read_busy, - o_read_queue_empty => read_empty, - o_read_queue_full => open, - i_start_transmission => start_uart_transmission, - i_write_data => write_data, - i_write_push => write_push, - o_write_busy => write_busy, - o_write_queue_empty => open, - o_write_queue_full => open - ); - -end Behavioral; diff --git a/ModNEF_Sources/modules/uart/uart_xstep.vhd b/ModNEF_Sources/modules/uart/uart_xstep.vhd index efa627517e6d45757e228a1550fe72c74c21bd6c..43ade23fb24a4abc96bd3e07499f0679eb360a17 100644 --- a/ModNEF_Sources/modules/uart/uart_xstep.vhd +++ b/ModNEF_Sources/modules/uart/uart_xstep.vhd @@ -196,7 +196,7 @@ begin end if; when wait_out_aer => - if i_emu_ready = '1' then + if i_emu_ready = '1' and network_to_uart_busy='0' then if read_empty = '1' then -- no more data to process start_uart_transmission <= '1'; emu_state <= send_aer; diff --git a/ModNEF_Sources/modules/uart/uart_xstep_timer.vhd b/ModNEF_Sources/modules/uart/uart_xstep_timer.vhd index e38d9b256bcfae0799a7d855243c3d98993b1489..53d5a403d800bb20f43c47fc359f7d19fc51dff2 100644 --- a/ModNEF_Sources/modules/uart/uart_xstep_timer.vhd +++ b/ModNEF_Sources/modules/uart/uart_xstep_timer.vhd @@ -220,7 +220,7 @@ begin end if; when wait_out_aer => - if i_emu_ready = '1' then + if i_emu_ready = '1' and network_to_uart_busy='0' then count_time <= '0'; if read_empty = '1' then -- no more data to process emu_state <= push_timer; diff --git a/modneflib/modnef/arch_builder/modules/BLIF/blif.py b/modneflib/modnef/arch_builder/modules/BLIF/blif.py index 4d5f09a6ff6741a947d87e0b931dc422d7b1864b..0caecf1e51726951047ea6f88f1a08202a8275d6 100644 --- a/modneflib/modnef/arch_builder/modules/BLIF/blif.py +++ b/modneflib/modnef/arch_builder/modules/BLIF/blif.py @@ -198,7 +198,8 @@ class BLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<bw) + two_comp(self.quantizer(weights[i][j]), bw) + + w_line = (w_line<<bw) + two_comp(self.quantizer(weights[i][j], unscale=False, clamp=True), bw) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") @@ -209,7 +210,7 @@ class BLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.weight_size) + self.quantizer(weights[i][j]) + w_line = (w_line<<self.weight_size) + self.quantizer(weights[i][j], unscale=False, clamp=True) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") diff --git a/modneflib/modnef/arch_builder/modules/BLIF/blif_debugger.py b/modneflib/modnef/arch_builder/modules/BLIF/blif_debugger.py index f6f7ca9881e1d6252aff9e5cdd10b15037aa1ad3..294001987bc026786cf2ce953f1cf52183b1dd93 100644 --- a/modneflib/modnef/arch_builder/modules/BLIF/blif_debugger.py +++ b/modneflib/modnef/arch_builder/modules/BLIF/blif_debugger.py @@ -213,7 +213,7 @@ class BLif_Debugger(ModNEFDebuggerMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") @@ -224,7 +224,7 @@ class BLif_Debugger(ModNEFDebuggerMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j]) + w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j], unscale=False, clamp=True) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") diff --git a/modneflib/modnef/arch_builder/modules/BLIF/rblif.py b/modneflib/modnef/arch_builder/modules/BLIF/rblif.py index 81f87c65b8ccbe95398c478cd6942ecdd21b4a40..d402fda94c19ba45b074b1adcaadb7ed7065a0cb 100644 --- a/modneflib/modnef/arch_builder/modules/BLIF/rblif.py +++ b/modneflib/modnef/arch_builder/modules/BLIF/rblif.py @@ -205,7 +205,7 @@ class RBLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") @@ -213,7 +213,7 @@ class RBLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j]) + w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j], unscale=False, clamp=True) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") @@ -224,7 +224,7 @@ class RBLif(ModNEFArchMod): for i in range(self.output_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(rec_weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(rec_weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) rec_mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") @@ -232,7 +232,7 @@ class RBLif(ModNEFArchMod): for i in range(self.output_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(rec_weights[i][j]) + w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(rec_weights[i][j], unscale=False, clamp=True) rec_mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") diff --git a/modneflib/modnef/arch_builder/modules/SLIF/rslif.py b/modneflib/modnef/arch_builder/modules/SLIF/rslif.py index 1c1561348cb39c35213b86b0ab2824b63767a7c1..b0a66a05f55d3038ef179d6ef54541924c53806e 100644 --- a/modneflib/modnef/arch_builder/modules/SLIF/rslif.py +++ b/modneflib/modnef/arch_builder/modules/SLIF/rslif.py @@ -214,14 +214,14 @@ class RSLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") else: for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j]) + w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j], unscale=False, clamp=True) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") mem_file.close() @@ -232,14 +232,14 @@ class RSLif(ModNEFArchMod): for i in range(self.output_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(rec_weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(rec_weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") else: for i in range(self.output_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(rec_weights[i][j]) + w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(rec_weights[i][j], unscale=False, clamp=True) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") mem_file.close() diff --git a/modneflib/modnef/arch_builder/modules/SLIF/slif.py b/modneflib/modnef/arch_builder/modules/SLIF/slif.py index ddf5c96f6e242224437fca9c8282bcdedcaf7e6e..991c16e5ed45a422b51199bf228697b87057e7ad 100644 --- a/modneflib/modnef/arch_builder/modules/SLIF/slif.py +++ b/modneflib/modnef/arch_builder/modules/SLIF/slif.py @@ -201,7 +201,7 @@ class SLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") self.v_threshold = two_comp(self.quantizer(self.v_threshold), self.variable_size) @@ -213,7 +213,7 @@ class SLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j]) + w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j], unscale=False, clamp=True) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") diff --git a/modneflib/modnef/arch_builder/modules/SLIF/slif_debugger.py b/modneflib/modnef/arch_builder/modules/SLIF/slif_debugger.py index 7fb587c792b02f91ceb9c375469e0d68b437062e..dfca4698ce362f8a6b979bf48ee49b1bc3b56640 100644 --- a/modneflib/modnef/arch_builder/modules/SLIF/slif_debugger.py +++ b/modneflib/modnef/arch_builder/modules/SLIF/slif_debugger.py @@ -215,7 +215,7 @@ class SLif_Debugger(ModNEFDebuggerMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") self.v_threshold = two_comp(self.quantizer(self.v_threshold), self.variable_size) @@ -227,7 +227,7 @@ class SLif_Debugger(ModNEFDebuggerMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j]) + w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j], unscale=False, clamp=True) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") diff --git a/modneflib/modnef/arch_builder/modules/ShiftLIF/rshiftlif.py b/modneflib/modnef/arch_builder/modules/ShiftLIF/rshiftlif.py index f3077ae9316df4d056ea3f2611867196c5adb71a..3d3427bc77b9619d1909c923ad9ee1ca6f6b5047 100644 --- a/modneflib/modnef/arch_builder/modules/ShiftLIF/rshiftlif.py +++ b/modneflib/modnef/arch_builder/modules/ShiftLIF/rshiftlif.py @@ -206,7 +206,7 @@ class RShiftLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") @@ -214,7 +214,7 @@ class RShiftLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer) + self.quantizer(weights[i][j]) + w_line = (w_line<<self.quantizer) + self.quantizer(weights[i][j], unscale=False, clamp=True) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") @@ -226,7 +226,7 @@ class RShiftLif(ModNEFArchMod): for i in range(self.output_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(rec_weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(rec_weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) rec_mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") @@ -234,7 +234,7 @@ class RShiftLif(ModNEFArchMod): for i in range(self.output_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(rec_weights[i][j]) + w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(rec_weights[i][j], unscale=False, clamp=True) rec_mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") diff --git a/modneflib/modnef/arch_builder/modules/ShiftLIF/shiftlif.py b/modneflib/modnef/arch_builder/modules/ShiftLIF/shiftlif.py index 929ed2612460c4901afae0cb5ac1c7b210cb9c6a..4c9b3e9f6d25e787521b98b72bad682b72c394ff 100644 --- a/modneflib/modnef/arch_builder/modules/ShiftLIF/shiftlif.py +++ b/modneflib/modnef/arch_builder/modules/ShiftLIF/shiftlif.py @@ -196,23 +196,25 @@ class ShiftLif(ModNEFArchMod): for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j]), self.quantizer.bitwidth) + w_line = (w_line<<self.quantizer.bitwidth) + two_comp(self.quantizer(weights[i][j], unscale=False, clamp=True), self.quantizer.bitwidth) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") - self.v_threshold = two_comp(self.quantizer(self.v_threshold), self.variable_size) + self.v_threshold = two_comp(self.quantizer(self.v_threshold, unscale=False, clamp=False), self.variable_size) + + else: for i in range(self.input_neuron): w_line = 0 for j in range(self.output_neuron-1, -1, -1): - w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j]) + w_line = (w_line<<self.quantizer.bitwidth) + self.quantizer(weights[i][j], unscale=False, clamp=True) mem_file.write(f"@{to_hex(i)} {to_hex(w_line)}\n") self.v_threshold = self.quantizer(self.v_threshold) - mem_file.close() + mem_file.close() def to_debugger(self, output_file : str = ""): """ diff --git a/modneflib/modnef/arch_builder/modules/UART/__init__.py b/modneflib/modnef/arch_builder/modules/UART/__init__.py index 1bf960420332ba13fce617145b3d49dc5165e617..3cbbe30479f8e038dc09b9a1db57ae6c9409eabc 100644 --- a/modneflib/modnef/arch_builder/modules/UART/__init__.py +++ b/modneflib/modnef/arch_builder/modules/UART/__init__.py @@ -7,7 +7,6 @@ Dependencies: uart_1step, uart_classifier, uart_classifier_timer, uart_xstep, ua Descriptions: UART module builder init """ -from .uart_1step import Uart_1Step from .uart_classifier import Uart_Classifier from .uart_classifier_timer import Uart_Classifier_Timer from .uart_xstep import Uart_XStep diff --git a/modneflib/modnef/arch_builder/modules/UART/uart_1step.py b/modneflib/modnef/arch_builder/modules/UART/uart_1step.py deleted file mode 100644 index 8fca341159282e2fb727e8025a2cde2f7b33fbc4..0000000000000000000000000000000000000000 --- a/modneflib/modnef/arch_builder/modules/UART/uart_1step.py +++ /dev/null @@ -1,242 +0,0 @@ -""" -File name: uart_1step -Author: Aurélie Saulquin -Version: 2.0.0 -License: GPL-3.0-or-later -Contact: aurelie.saulquin@univ-lille.fr -Dependencies: io_arch, yaml -Descriptions: UART_1Step ModNEF archbuilder module -""" - -from ..io_arch import IOArch -import yaml - -_UART_1STEP_DEFINITION = """ - component uart_1Step is - generic( - clk_freq : integer := 100_000_000; - baud_rate : integer := 115_200; - - queue_read_depth : integer := 32; - queue_read_type : string := "fifo"; - - queue_write_type : string := "fifo"; - - input_layer_size : integer := 8; - output_layer_size : integer := 8 - ); - port( - i_clk : in std_logic; - i_en : in std_logic; - - i_tx : in std_logic; - o_rx : out std_logic; - - i_emu_ready : in std_logic; - o_start_emu : out std_logic; - o_reset_membrane : out std_logic; - - i_req : in std_logic; - o_ack : out std_logic; - i_emu_busy : in std_logic; - i_spike_flag : in std_logic; - i_aer : in std_logic_vector(log_b(output_layer_size, 2)-1 downto 0); - - o_req : out std_logic; - i_ack : in std_logic; - o_emu_busy : out std_logic; - o_spike_flag : out std_logic; - o_aer : out std_logic_vector(log_b(input_layer_size, 2)-1 downto 0) - ); - end component; -""" - -class Uart_1Step(IOArch): - """ - Uart_1Step module class - Each UART transmission correspond to an emulation step - - Attributes - ---------- - name : str - name of module - input_layer_size : int - size in neurons of input layer - output_layer_size : int - size in neurons of output layer - clk_freq : int - board clock frequency - baud_rate : int - data baud rate - queue_read_type : int - type of reaad queue - queue_write_type : int - type of write queue - tx_name : str - name of tx signal - rx_name : str - name of rx signal - - Methods - ------- - vhdl_component_name() - return component name - vhdl_component_definition() - return vhdl component definition - to_vhdl(vhdl_file, pred, suc, clock_name): - write vhdl component instanciation - to_yaml(file): - generate yaml configuration file for driver - write_io(vhdl_file): - write signals into entity definition section - """ - - def __init__(self, - name: str, - input_layer_size: int, - output_layer_size: int, - clk_freq: int, - baud_rate: int, - tx_name: str, - rx_name: str, - queue_read_depth : int, - queue_read_type: str = "fifo", - queue_write_type: str = "fifo" - ): - """ - Initialize attributes - - Parameters - ---------- - name : str - name of module - clk_freq : int - board clock frequency - baud_rate : int - data baud rate - tx_name : str - name of tx signal - rx_name : str - name of rx signal - input_layer_size : int = -1 - size in neurons of input layer - output_layer_size : int = -1 - size in neurons of output layer - queue_read_type : str = "fifo" - read queue type : "fifo" or "lifo" - queue_write_type : str = "fifo" - write queue type : "fifo" or "lifo" - """ - - self.name = name - - self.input_neuron = output_layer_size - self.output_neuron = input_layer_size - - self.input_layer_size = input_layer_size - self.output_layer_size = output_layer_size - - self.clk_freq = clk_freq - self.baud_rate = baud_rate - - self.queue_read_type = queue_read_type - self.queue_read_depth = queue_read_depth - self.queue_write_type = queue_write_type - - self.tx_name = tx_name - self.rx_name = rx_name - - def vhdl_component_name(self): - """ - Module identifier use during component definition - - Returns - ------- - str - """ - - return "Uart_1_Step" - - - def vhdl_component_definition(self): - """ - VHDL component definition - - Returns - ------- - str - """ - - return _UART_1STEP_DEFINITION - - def to_vhdl(self, vhdl_file, pred, suc, clock_name): - """ - Write vhdl componenent - - Parameters - ---------- - vhdl_file : TextIOWrapper - vhdl file - pred : List of ModNEFArchMod - list of predecessor module (1 pred for this module) - suc : List of ModNEFArchMod - list of successor module (1 suc for this module) - clock_name : str - clock signal name - """ - - vhdl_file.write(f"\t{self.name} : uart_1step generic map(\n") - vhdl_file.write(f"\t\tclk_freq => {self.clk_freq},\n") - vhdl_file.write(f"\t\tbaud_rate => {self.baud_rate},\n") - vhdl_file.write(f"\t\tqueue_read_depth => {self.queue_read_depth},\n") - vhdl_file.write(f"\t\tqueue_read_type => \"{self.queue_read_type}\",\n") - vhdl_file.write(f"\t\tqueue_write_type => \"{self.queue_write_type}\",\n") - vhdl_file.write(f"\t\tinput_layer_size => {self.input_layer_size},\n") - vhdl_file.write(f"\t\toutput_layer_size => {self.output_layer_size}\n") - vhdl_file.write(f"\t) port map(\n") - vhdl_file.write(f"\t\ti_clk => {clock_name},\n") - vhdl_file.write("\t\ti_en => '1',\n") - vhdl_file.write(f"\t\ti_tx => {self.tx_name},\n") - vhdl_file.write(f"\t\to_rx => {self.rx_name},\n") - vhdl_file.write("\t\ti_emu_ready => emu_ready,\n") - vhdl_file.write("\t\to_start_emu => start_emu,\n") - vhdl_file.write("\t\to_reset_membrane => reset_membrane,\n") - self._write_port_map(vhdl_file, pred[0].name, self.name, "in", "", False) - self._write_port_map(vhdl_file, self.name, suc[0].name, "out", "", True) - vhdl_file.write("\t);\n") - - def to_yaml(self, file): - """ - Generate yaml driver description file - - Parameters - ---------- - file : str - configuration file name - """ - d = {} - - - - d["module"] = "1Step" - d["input_layer_size"] = self.input_layer_size - d["output_layer_size"] = self.output_layer_size - d["baud_rate"] = self.baud_rate - d["queue_read_depth"] = self.queue_read_depth - d["queue_write_depth"] = self.output_layer_size - - with open(file, 'w') as f: - yaml.dump(d, f) - - def write_io(self, vhdl_file): - """ - Write port IO in entity definition section - - Parameters - ---------- - vhdl_file : TextIOWrapper - vhdl file - """ - - vhdl_file.write(f"\t\t{self.tx_name} : in std_logic;\n") - vhdl_file.write(f"\t\t{self.rx_name} : out std_logic\n") diff --git a/modneflib/modnef/modnef_driver/drivers/__init__.py b/modneflib/modnef/modnef_driver/drivers/__init__.py index 0615fd915b881e40174ffa9e4a47c16cffbd1143..1bd6c7110c70072bc76e93d6790e6f290b03b768 100644 --- a/modneflib/modnef/modnef_driver/drivers/__init__.py +++ b/modneflib/modnef/modnef_driver/drivers/__init__.py @@ -11,6 +11,5 @@ from .classifier_driver import Classifier_Driver from .classifier_timer_driver import Classifier_Timer_Driver from .debugger_driver import Debugger_Driver from .default_driver import ModNEF_Driver -from .single_step_driver import SingleStep_Driver from .xstep_driver import XStep_Driver from .xstep_timer_driver import XStep_Timer_Driver \ No newline at end of file diff --git a/modneflib/modnef/modnef_driver/drivers/single_step_driver.py b/modneflib/modnef/modnef_driver/drivers/single_step_driver.py deleted file mode 100644 index 0feba5e3733e4253d7314b02497c62b89c23a3af..0000000000000000000000000000000000000000 --- a/modneflib/modnef/modnef_driver/drivers/single_step_driver.py +++ /dev/null @@ -1,136 +0,0 @@ -""" -File name: single_step_driver -Author: Aurélie Saulquin -Version: 2.0.0 -License: GPL-3.0-or-later -Contact: aurelie.saulquin@univ-lille.fr -Dependencies: default_driver, yaml -Descriptions: Driver class for UART_1Step uart module -""" - -from .default_driver import default_transformation -from .default_driver import ModNEF_Driver, ClosedDriverError -import yaml - -class SingleStep_Driver(ModNEF_Driver): - """ - Driver of Uart_SingleStep module - - Attributes - ---------- - board_path : str - fpga driver board path - baud_rate : int - data baud rate - input_layer_size : int - number of neuron in input layer - output_layer_size : int - number of neuron in output layer - queue_read_depth : int - number of word of read queue - queue_write_depth : int - number of word of write queue - - Methods - ------- - from_yaml(yaml_file, board_path) : classmethod - create driver from yaml configuration file - run_sample(input_sample, transformation, reset_membrane): - run data communication to run a data sample - """ - - def __init__(self, board_path, baud_rate, input_layer_size, output_layer_size, queue_read_depth, queue_write_depth): - """ - Constructor - - Parameters - ---------- - board_path : str - fpga driver board path - baud_rate : int - data baud rate - input_layer_size : int - number of neuron in input layer - output_layer_size : int - number of neuron in output layer - queue_read_depth : int - number of word of read queue - queue_write_depth : int - number of word of write queue - """ - - - - super().__init__(board_path, baud_rate, input_layer_size, output_layer_size, queue_read_depth, queue_write_depth) - - @classmethod - def from_yaml(cls, yaml_file, board_path): - """ - classmethod - - create driver from driver configuration file - - Parameters - ---------- - yaml_file : str - configuration file - board_path : str - path to board driver - """ - - - - with open(yaml_file, 'r') as f: - config = yaml.safe_load(f) - - print("coucou") - - d = cls(board_path = board_path, - baud_rate = config["baud_rate"], - input_layer_size = config["input_layer_size"], - output_layer_size = config["output_layer_size"], - queue_read_depth = config["queue_read_depth"], - queue_write_depth = config["queue_write_depth"] - ) - return d - - - def run_sample(self, input_sample, transformation = default_transformation, reset_membrane=False, extra_step = 0): - """ - Run an entire data sample by using run_step function (for more details see run_step) - - Parameters - ---------- - input_sample : list - list of spikes of sample - transformation : function - function call to tranform input spikes to AER representation - reset_membrane : bool - set to true if reset voltage membrane after sample transmission - - Returns - ------- - list of list of int: - list of list of output AER data for all emulation step - """ - - if self._is_close: - raise ClosedDriverError() - - sample_res = [0 for _ in range(self.output_layer_size)] - - sample_aer = [transformation(s) for s in input_sample] - - for es in range(extra_step): - sample_aer.append([]) - - for step in range(len(input_sample)): - step_spikes = sample_aer[step] - if step == len(input_sample)-1: - step_res = self.rust_driver.data_transmission(step_spikes, reset_membrane) - else: - step_res = self.rust_driver.data_transmission(step_spikes, False) - for s in step_res: - sample_res[s] += 1 - - return sample_res diff --git a/modneflib/modnef/modnef_driver/drivers/xstep_driver.py b/modneflib/modnef/modnef_driver/drivers/xstep_driver.py index 778b95b1a72a37f0cd6a6a61a0e57aa3c37baad9..1f6d31ab51ff79cad91c1a65ac604b04625b1e7e 100644 --- a/modneflib/modnef/modnef_driver/drivers/xstep_driver.py +++ b/modneflib/modnef/modnef_driver/drivers/xstep_driver.py @@ -161,7 +161,6 @@ class XStep_Driver(ModNEF_Driver): if step == len(sample_aer)-1: emulation_result = self.rust_driver.data_transmission(data, reset_membrane) - res_step = self._unpack_data(emulation_result) for rs in res_step: for aer in rs: diff --git a/modneflib/modnef/modnef_driver/drivers/xstep_timer_driver.py b/modneflib/modnef/modnef_driver/drivers/xstep_timer_driver.py index 58de027609fb796bc97afccf849716105446ae7e..ebb23577999a295b418abf784e71ce968055735e 100644 --- a/modneflib/modnef/modnef_driver/drivers/xstep_timer_driver.py +++ b/modneflib/modnef/modnef_driver/drivers/xstep_timer_driver.py @@ -144,7 +144,7 @@ class XStep_Timer_Driver(ModNEF_Driver): sample_aer = [transformation(input_sample[step]) for step in range(len(input_sample))] for es in range(extra_step): - sample_aer.append([]) + sample_aer.append([0, 1, 2, 3]) step_send = 0 res = [0 for _ in range(self.output_layer_size)] @@ -155,11 +155,11 @@ class XStep_Timer_Driver(ModNEF_Driver): for step in range(len(sample_aer)): next_data = [] + if(len(sample_aer[step])+len(next_data) > 256**self.queue_read_width): + print(f"warning, the read queue cannot encode the len of emulation step : acutal len {len(sample_aer[step])}, maximum len {256**self.qeue_read_width}") next_data.append(len(sample_aer[step])) next_data.extend(sample_aer[step]) - if(len(sample_aer[step]) > 256**self.queue_read_width): - print(f"warning, the read queue cannot encode the len of emulation step : acutal len {len(sample_aer[step])}, maximum len {256**self.qeue_read_width}") - if len(data) + len(next_data) > self.queue_read_depth or (step_send+1)*self.output_layer_size > self.queue_write_depth-2: + if len(data) + len(next_data) > self.queue_read_depth or step_send*self.output_layer_size > self.queue_write_depth-2: emulation_result = self.rust_driver.data_transmission(data, False) res_step = self._unpack_data(emulation_result) @@ -201,8 +201,10 @@ class XStep_Timer_Driver(ModNEF_Driver): #print((data[0]*256 + data[1])*self.clock_period) + self.sample_time += (data[0]*256 + data[1])*self.clock_period + while index < len(data): n_data = data[index] index += 1 diff --git a/modneflib/modnef/modnef_driver/modnef_drivers.py b/modneflib/modnef/modnef_driver/modnef_drivers.py index be6f4db13049d828351fb97c5cb652a25359bc7f..11e2cc3226bae31637382a1f47a9c2f7fc03420f 100644 --- a/modneflib/modnef/modnef_driver/modnef_drivers.py +++ b/modneflib/modnef/modnef_driver/modnef_drivers.py @@ -13,7 +13,6 @@ from .drivers import * import yaml drivers_dict = { - "1Step" : SingleStep_Driver, "XStep" : XStep_Driver, "Classifier" : Classifier_Driver, "Debugger" : Debugger_Driver, diff --git a/modneflib/modnef/modnef_torch/__init__.py b/modneflib/modnef/modnef_torch/__init__.py index e57895ff347f15ba130ffa2d510ba213d5e63f39..2053dab5330a19f29ea447f8c285fdeaf2d60ad5 100644 --- a/modneflib/modnef/modnef_torch/__init__.py +++ b/modneflib/modnef/modnef_torch/__init__.py @@ -9,4 +9,5 @@ Descriptions: ModNEF torch lib definition from .modnef_neurons import * from .model_builder import ModNEFModelBuilder -from .model import ModNEFModel \ No newline at end of file +from .model import ModNEFModel +from .quantLinear import QuantLinear \ No newline at end of file diff --git a/modneflib/modnef/modnef_torch/model.py b/modneflib/modnef/modnef_torch/model.py index 44443dc4da4759f9c0c785f4df1e2b3c9493cca2..f63da283c435d9340cfe85b4a6d20275fdac0e9b 100644 --- a/modneflib/modnef/modnef_torch/model.py +++ b/modneflib/modnef/modnef_torch/model.py @@ -1,17 +1,15 @@ """ File name: model Author: Aurélie Saulquin -Version: 1.0.0 +Version: 1.1.0 License: GPL-3.0-or-later Contact: aurelie.saulquin@univ-lille.fr -Dependencies: torch, snntorch, modnef.archbuilder, modnef_torch_neuron +Dependencies: torch, snntorch, modnef_torch_neuron, modnef_driver Descriptions: ModNEF SNN Model """ -import modnef.modnef_torch.modnef_neurons as mn import torch.nn as nn import torch -from modnef.arch_builder import * from modnef.modnef_driver import load_driver_from_yaml from modnef.modnef_torch.modnef_neurons import ModNEFNeuron @@ -88,11 +86,52 @@ class ModNEFModel(nn.Module): for m in self.modules(): if isinstance(m, ModNEFNeuron): m.hardware_estimation(hardware) - m.set_quant(quant) + m.run_quantize(quant) return super().train(mode=mode) - def quantize(self, force_init=False): + def init_quantizer(self): + """ + initialize quantizer of laters + """ + + for m in self.modules(): + if isinstance(m, ModNEFNeuron): + m.init_quantizer() + + def quantize_hp(self, force_init=False): + """ + Quantize neuron hyper parameters + + Parameters + ---------- + force_init = False : bool + force quantizer initialization + """ + + for m in self.modules(): + if isinstance(m, ModNEFNeuron): + if force_init: + m.init_quantizer() + m.quantize_hp() + + def quantize_weight(self, force_init=False): + """ + Quantize synaptic weight + + Parameters + ---------- + force_init = False : bool + force quantizer initialization + """ + + for m in self.modules(): + if isinstance(m, ModNEFNeuron): + if force_init: + m.init_quantizer() + m.quantize_weight() + + def quantize(self, force_init=False, clamp=False): """ Quantize synaptic weight and neuron hyper-parameters @@ -104,13 +143,21 @@ class ModNEFModel(nn.Module): for m in self.modules(): if isinstance(m, ModNEFNeuron): - m.quantize(force_init=force_init) + m.quantize(force_init=force_init, clamp=clamp) + + def clamp(self, force_init=False): + """ + Clamp synaptic weight with quantizer born - def clamp(self): + Parameters + ---------- + force_init = False : bool + force quantizer initialization + """ for m in self.modules(): if isinstance(m, ModNEFNeuron): - m.clamp() + m.clamp(force_init=force_init) def train(self, mode : bool = True, quant : bool = False): """ @@ -151,6 +198,7 @@ class ModNEFModel(nn.Module): if self.driver != None: self.driver.close() + self.driver = None def forward(self, input_spikes): """ diff --git a/modneflib/modnef/modnef_torch/modnef_neurons/blif_model/blif.py b/modneflib/modnef/modnef_torch/modnef_neurons/blif_model/blif.py index d223c86d20afea7be6e97ceec852ed2fb7346009..99b6154532bfe2b6d09c4852c5b7db8c7f1a1963 100644 --- a/modneflib/modnef/modnef_torch/modnef_neurons/blif_model/blif.py +++ b/modneflib/modnef/modnef_torch/modnef_neurons/blif_model/blif.py @@ -1,7 +1,7 @@ """ File name: blif Author: Aurélie Saulquin -Version: 1.1.0 +Version: 1.2.1 License: GPL-3.0-or-later Contact: aurelie.saulquin@univ-lille.fr Dependencies: torch, math, snntorch, modnef.archbuilder, modnef_torch_neuron, modnef.quantizer @@ -9,16 +9,14 @@ Descriptions: ModNEF torch BLIF neuron model Based on snntorch.Leaky and snntorch.LIF class """ -import torch.nn as nn import torch from math import log, ceil -from snntorch import Leaky import modnef.arch_builder as builder from modnef.arch_builder.modules.utilities import * from ..modnef_torch_neuron import ModNEFNeuron, _quantizer from modnef.quantizer import * -class BLIF(Leaky, ModNEFNeuron): +class BLIF(ModNEFNeuron): """ ModNEFTorch BLIF neuron model @@ -111,26 +109,15 @@ class BLIF(Leaky, ModNEFNeuron): quantization method """ - Leaky.__init__( - self=self, - beta=beta, - threshold=threshold, - spike_grad=spike_grad, - surrogate_disable=False, - init_hidden=False, - inhibition=False, - learn_beta=False, - learn_threshold=False, - reset_mechanism=reset_mechanism, - state_quant=False, - output=False, - graded_spikes_factor=1.0, - learn_graded_spikes_factor=False, - ) - - ModNEFNeuron.__init__(self=self, quantizer=quantizer) - - self.fc = nn.Linear(in_features, out_features, bias=False) + super().__init__(threshold=threshold, + in_features=in_features, + out_features=out_features, + reset_mechanism=reset_mechanism, + spike_grad=spike_grad, + quantizer=quantizer + ) + + self.register_buffer("beta", torch.tensor(beta)) self._init_mem() @@ -234,27 +221,30 @@ class BLIF(Leaky, ModNEFNeuron): if not spk==None: self.spk = spk - input_ = self.fc(input_) + quant = self.quantizer if self.quantization_flag else None - if not self.mem.shape == input_.shape: - self.mem = torch.zeros_like(input_, device=self.mem.device) - - if self.quantization_flag: - input_.data = self.quantizer(input_.data, True) - self.mem.data = self.quantizer(self.mem.data, True) + forward_current = self.fc(input_, quant) + if not self.mem.shape == forward_current.shape: + self.mem = torch.zeros_like(forward_current, device=self.mem.device) + self.reset = self.mem_reset(self.mem) + self.mem = self.mem + forward_current + if self.reset_mechanism == "subtract": - self.mem = (self.mem+input_)*self.beta-self.reset*self.threshold + self.mem = self.mem-self.reset*self.threshold elif self.reset_mechanism == "zero": - self.mem = (self.mem+input_)*self.beta-self.reset*self.mem - else: - self.mem = self.mem*self.beta + self.mem = self.mem-self.reset*self.mem if self.hardware_estimation_flag: - self.val_min = torch.min(torch.min(input_.min(), self.mem.min()), self.val_min) - self.val_max = torch.max(torch.max(input_.max(), self.mem.max()), self.val_max) + self.val_min = torch.min(self.mem.min(), self.val_min).detach() + self.val_max = torch.max(self.mem.max(), self.val_max).detach() + + self.mem = self.mem*self.beta + + if self.quantization_flag: + self.mem.data = QuantizeSTE.apply(self.mem, self.quantizer) self.spk = self.fire(self.mem) @@ -275,6 +265,7 @@ class BLIF(Leaky, ModNEFNeuron): ------- BLIF """ + if self.hardware_description["variable_size"]==-1: if self.hardware_estimation_flag: @@ -304,50 +295,15 @@ class BLIF(Leaky, ModNEFNeuron): output_path=output_path ) return module - - def quantize_weight(self): - """ - Quantize synaptic weight - """ - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight) - - self.fc.weight.data = self.quantizer(self.fc.weight.data, True) - - def quantize_parameters(self): - """ - Quantize neuron hyper-parameters - """ - - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight) - - self.threshold.data = self.quantizer(self.threshold.data, True) - self.beta.data = self.quantizer(self.beta.data, True) - - def quantize(self, force_init=False): - """ - Quantize synaptic weight and neuron hyper-parameters - - Parameters - ---------- - force_init = Fasle : bool - force quantizer initialization - """ - - if force_init or not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight) - - self.quantize_weight() - self.quantize_parameters() - self.quantization_flag = True - def clamp(self): + def quantize_hp(self): """ - Clamp synaptic weight and neuron hyper-parameters + neuron hyper-parameters quantization. + We assume you already initialize quantizer """ - self.fc.weight.data = self.quantizer.clamp(self.fc.weight.data) + self.threshold.data = QuantizeSTE.apply(self.threshold, self.quantizer) + self.beta.data = QuantizeSTE.apply(self.beta, self.quantizer) @classmethod def detach_hidden(cls): diff --git a/modneflib/modnef/modnef_torch/modnef_neurons/blif_model/rblif.py b/modneflib/modnef/modnef_torch/modnef_neurons/blif_model/rblif.py index d48960ddde25bfcb3a8c02ae049cc1fafe93a86e..f930deb08db239d9e47b64cbea9f2e527e4b6b51 100644 --- a/modneflib/modnef/modnef_torch/modnef_neurons/blif_model/rblif.py +++ b/modneflib/modnef/modnef_torch/modnef_neurons/blif_model/rblif.py @@ -1,7 +1,7 @@ """ File name: rblif Author: Aurélie Saulquin -Version: 1.1.0 +Version: 1.2.1 License: GPL-3.0-or-later Contact: aurelie.saulquin@univ-lille.fr Dependencies: torch, snntorch, modnef.archbuilder, modnef_torch_neuron, math, modnef.quantizer @@ -9,16 +9,16 @@ Descriptions: ModNEF torch reccurrent BLIF neuron model Based on snntorch.RLeaky and snntorch.LIF class """ -import torch.nn as nn import torch -from snntorch import Leaky import modnef.arch_builder as builder from modnef.arch_builder.modules.utilities import * from ..modnef_torch_neuron import ModNEFNeuron, _quantizer from math import log, ceil from modnef.quantizer import * +from modnef.modnef_torch.quantLinear import QuantLinear -class RBLIF(Leaky, ModNEFNeuron): + +class RBLIF(ModNEFNeuron): """ ModNEFTorch reccurent BLIF neuron model @@ -116,27 +116,17 @@ class RBLIF(Leaky, ModNEFNeuron): quantization method """ - Leaky.__init__( - self=self, - beta=beta, - threshold=threshold, - spike_grad=spike_grad, - surrogate_disable=False, - init_hidden=False, - inhibition=False, - learn_beta=False, - learn_threshold=False, - reset_mechanism=reset_mechanism, - state_quant=False, - output=False, - graded_spikes_factor=1.0, - learn_graded_spikes_factor=False, - ) - - ModNEFNeuron.__init__(self=self, quantizer=quantizer) + super().__init__(threshold=threshold, + in_features=in_features, + out_features=out_features, + reset_mechanism=reset_mechanism, + spike_grad=spike_grad, + quantizer=quantizer + ) + + self.register_buffer("beta", torch.tensor(beta)) - self.fc = nn.Linear(in_features, out_features, bias=False) - self.reccurent = nn.Linear(out_features, out_features, bias=False) + self.reccurent = QuantLinear(out_features, out_features) self._init_mem() @@ -241,33 +231,35 @@ class RBLIF(Leaky, ModNEFNeuron): if not spk == None: self.spk = spk - input_ = self.fc(input_) + quant = self.quantizer if self.quantization_flag else None - if not self.mem.shape == input_.shape: - self.mem = torch.zeros_like(input_, device=self.mem.device) + forward_current = self.fc(input_, quant) - if not self.spk.shape == input_.shape: - self.spk = torch.zeros_like(input_, device=self.spk.device) + if not self.mem.shape == forward_current.shape: + self.mem = torch.zeros_like(forward_current, device=self.mem.device) - self.reset = self.mem_reset(self.mem) + if not self.spk.shape == forward_current.shape: + self.spk = torch.zeros_like(forward_current, device=self.spk.device) - rec = self.reccurent(self.spk) + self.reset = self.mem_reset(self.mem) + + rec_current = self.reccurent(self.spk, quant) - if self.quantization_flag: - self.mem.data = self.quantizer(self.mem.data, True) - input_.data = self.quantizer(input_.data, True) - rec.data = self.quantizer(rec.data, True) + self.mem = self.mem + forward_current + rec_current if self.reset_mechanism == "subtract": - self.mem = (self.mem+input_+rec)*self.beta-self.reset*self.threshold + self.mem = self.mem-self.reset*self.threshold elif self.reset_mechanism == "zero": - self.mem = (self.mem+input_+rec)*self.beta-self.reset*self.mem - else: - self.mem = self.mem*self.beta + self.mem = self.mem-self.reset*self.mem if self.hardware_estimation_flag: - self.val_min = torch.min(torch.min(input_.min(), self.mem.min()), self.val_min) - self.val_max = torch.max(torch.max(input_.max(), self.mem.max()), self.val_max) + self.val_min = torch.min(self.mem.min(), self.val_min).detach() + self.val_max = torch.max(self.mem.max(), self.val_max).detach() + + self.mem = self.mem*self.beta + + if self.quantization_flag: + self.mem.data = QuantizeSTE.apply(self.mem, self.quantizer) self.spk = self.fire(self.mem) @@ -318,52 +310,14 @@ class RBLIF(Leaky, ModNEFNeuron): ) return module - def quantize_weight(self): - """ - Quantize synaptic weight - """ - - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight, self.reccurent.weight) - - self.fc.weight.data = self.quantizer(self.fc.weight.data, True) - self.reccurent.weight.data = self.quantizer(self.reccurent.weight.data, True) - - def quantize_parameters(self): - """ - Quantize neuron hyper-parameters - """ - - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight, self.reccurent.weight) - - self.threshold.data = self.quantizer(self.threshold.data, True) - self.beta.data = self.quantizer(self.beta.data, True) - - def quantize(self, force_init=False): - """ - Quantize synaptic weight and neuron hyper-parameters - - Parameters - ---------- - force_init = Fasle : bool - force quantizer initialization - """ - - if force_init or not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight, self.reccurent.weight) - - self.quantize_weight() - self.quantize_parameters() - self.quantization_flag = True - - def clamp(self): + def quantize_hp(self): """ - Clamp synaptic weight and neuron hyper-parameters + neuron hyper-parameters quantization. + We assume you already initialize quantizer """ - self.fc.weight.data = self.quantizer.clamp(self.fc.weight.data) - self.reccurent.weight.data = self.quantizer.clamp(self.reccurent.weight.data) + self.threshold.data = QuantizeSTE.apply(self.threshold, self.quantizer) + self.beta.data = QuantizeSTE.apply(self.beta, self.quantizer) @classmethod diff --git a/modneflib/modnef/modnef_torch/modnef_neurons/modnef_torch_neuron.py b/modneflib/modnef/modnef_torch/modnef_neurons/modnef_torch_neuron.py index 73225949001afeb0d5700f6f4a5c15c78b2079aa..681b88aa426edc61ceaba9377e5a7f10da48c19f 100644 --- a/modneflib/modnef/modnef_torch/modnef_neurons/modnef_torch_neuron.py +++ b/modneflib/modnef/modnef_torch/modnef_neurons/modnef_torch_neuron.py @@ -1,7 +1,7 @@ """ File name: modnef_torch_neuron Author: Aurélie Saulquin -Version: 1.0.0 +Version: 1.0.1 License: GPL-3.0-or-later Contact: aurelie.saulquin@univ-lille.fr Dependencies: torch @@ -9,7 +9,10 @@ Descriptions: ModNEF torch neuron interface builder """ import torch +import torch.nn as nn from modnef.quantizer import * +from snntorch._neurons import SpikingNeuron +from ..quantLinear import QuantLinear _quantizer = { "FixedPointQuantizer" : FixedPointQuantizer, @@ -17,7 +20,7 @@ _quantizer = { "DynamicScaleFactorQuantizer" : DynamicScaleFactorQuantizer } -class ModNEFNeuron(): +class ModNEFNeuron(SpikingNeuron): """ ModNEF torch neuron interface @@ -42,7 +45,24 @@ class ModNEFNeuron(): create and return the corresponding modnef archbuilder module from internal neuron parameters """ - def __init__(self, quantizer : Quantizer): + def __init__(self, + in_features, + out_features, + threshold, + reset_mechanism, + spike_grad, + quantizer): + + super().__init__( + threshold=threshold, + spike_grad=spike_grad, + reset_mechanism=reset_mechanism + ) + + self.fc = QuantLinear(in_features=in_features, out_features=out_features) + #self.fc = nn.Linear(in_features=in_features, out_features=out_features, bias=False) + + self.hardware_estimation_flag = False self.quantization_flag = False @@ -53,6 +73,9 @@ class ModNEFNeuron(): self.quantizer = quantizer + + self.alpha = 0.9 + @classmethod def from_dict(cls, dict): """ @@ -61,7 +84,19 @@ class ModNEFNeuron(): raise NotImplementedError() - def quantize_weight(self): + def init_quantizer(self): + """ + Initialize internal or re-initialize internal quantizer + """ + + param = list(self.parameters()) + + if len(param)==1: + self.quantizer.init_from_weight(param[0]) + else: + self.quantizer.init_from_weight(param[0], param[1]) + + def quantize_weight(self, clamp=False): """ synaptic weight quantization @@ -70,16 +105,19 @@ class ModNEFNeuron(): NotImplementedError() """ - raise NotImplementedError() + for p in self.parameters(): + p.data = QuantizeSTE.apply(p.data, self.quantizer, clamp) + - def quantize_parameters(self): + def quantize_hp(self): """ neuron hyper-parameters quantization + We assume you've already intialize quantizer """ raise NotImplementedError() - def quantize(self, force_init=False): + def quantize(self, force_init=False, clamp=False): """ Quantize synaptic weight and neuron hyper-parameters @@ -89,16 +127,38 @@ class ModNEFNeuron(): force quantizer initialization """ - raise NotImplementedError() + if force_init: + self.init_quantizer() + + self.quantize_weight(clamp=clamp) + self.quantize_hp() - def clamp(self): + def clamp(self, force_init=False): """ Clamp synaptic weight + + Parameters + ---------- + force_init = Fasle : bool + force quantizer initialization """ - raise NotImplementedError() + if force_init: + self.init_quantizer() + + for p in self.parameters(): + p.data = self.quantizer.clamp(p.data) - def set_quant(self, mode=False): + def run_quantize(self, mode=False): + """ + Srtup quantization flag + + Parameters + ---------- + mode : bool = False + quantize run or not + """ + self.quantization_flag = mode def hardware_estimation(self, mode = False): diff --git a/modneflib/modnef/modnef_torch/modnef_neurons/slif_model/rslif.py b/modneflib/modnef/modnef_torch/modnef_neurons/slif_model/rslif.py index 694971edc6b2b40a4b7d5a2f83760d45095fd043..895adcef790cd56f99090a539b4f7d535fbf76a0 100644 --- a/modneflib/modnef/modnef_torch/modnef_neurons/slif_model/rslif.py +++ b/modneflib/modnef/modnef_torch/modnef_neurons/slif_model/rslif.py @@ -1,7 +1,7 @@ """ File name: rslif Author: Aurélie Saulquin -Version: 1.1.0 +Version: 1.2.1 License: GPL-3.0-or-later Contact: aurelie.saulquin@univ-lille.fr Dependencies: torch, snntorch, modnef.archbuilder, modnef_torch_neuron, math, modnef.quantizer @@ -9,16 +9,15 @@ Descriptions: ModNEF torch reccurent SLIF neuron model Based on snntorch.RLeaky and snntroch.LIF class """ -import torch.nn as nn import torch -from snntorch import LIF import modnef.arch_builder as builder from modnef.arch_builder.modules.utilities import * from ..modnef_torch_neuron import ModNEFNeuron, _quantizer from math import ceil, log -from modnef.quantizer import MinMaxQuantizer +from modnef.quantizer import MinMaxQuantizer, QuantizeSTE +from modnef.modnef_torch.quantLinear import QuantLinear -class RSLIF(LIF, ModNEFNeuron): +class RSLIF(ModNEFNeuron): """ ModNEFTorch reccurent Simplifed LIF neuron model @@ -119,34 +118,19 @@ class RSLIF(LIF, ModNEFNeuron): quantization function """ - LIF.__init__( - self=self, - beta = v_leak, - threshold=threshold, - spike_grad=spike_grad, - surrogate_disable=False, - init_hidden=False, - inhibition=False, - learn_beta=False, - learn_threshold=False, - reset_mechanism="zero", - state_quant=False, - output=False, - graded_spikes_factor=1.0, - learn_graded_spikes_factor=False - ) - - ModNEFNeuron.__init__(self, quantizer=quantizer) + super().__init__(threshold=threshold, + in_features=in_features, + out_features=out_features, + reset_mechanism="zero", + spike_grad=spike_grad, + quantizer=quantizer + ) self.register_buffer("v_leak", torch.as_tensor(v_leak)) self.register_buffer("v_min", torch.as_tensor(v_min)) self.register_buffer("v_rest", torch.as_tensor(v_rest)) - self.in_features = in_features - self.out_features = out_features - - self.fc = nn.Linear(self.in_features, self.out_features, bias=False) - self.reccurent = nn.Linear(self.out_features, self.out_features, bias=False) + self.reccurent = QuantLinear(out_features, out_features) self._init_mem() @@ -253,42 +237,41 @@ class RSLIF(LIF, ModNEFNeuron): if not spk == None: self.spk = spk - - input_ = self.fc(input_) + quant = self.quantizer if self.quantization_flag else None + + forward_current = self.fc(input_, quant) + + if not self.mem.shape == forward_current.shape: + self.mem = torch.ones_like(forward_current, device=self.mem.device)*self.v_rest + + if not self.spk.shape == forward_current.shape: + self.spk = torch.zeros_like(forward_current, device=self.spk.device) - if not self.mem.shape == input_.shape: - self.mem = torch.ones_like(input_)*self.v_rest - - if not self.spk.shape == input_.shape: - self.spk = torch.zeros_like(input_) self.reset = self.mem_reset(self.mem) - rec_input = self.reccurent(self.spk) + rec_current = self.reccurent(self.spk, quant) - if self.quantization_flag: - input_.data = self.quantizer(input_.data, True) - rec_input.data = self.quantizer(rec_input.data, True) - self.mem = self.quantizer(self.mem.data, True) + self.mem = self.mem + forward_current + rec_current - self.mem = self.mem + input_ + rec_input + self.mem = self.mem-self.reset*(self.mem-self.v_rest) if self.hardware_estimation_flag: - self.val_min = torch.min(torch.min(input_.min(), self.mem.min()), self.val_min) - self.val_max = torch.max(torch.max(input_.max(), self.mem.max()), self.val_max) + self.val_min = torch.min(self.mem.min(), self.val_min).detach() + self.val_max = torch.max(self.mem.max(), self.val_max).detach() + # update neuron self.mem = self.mem - self.v_leak + min_reset = (self.mem<self.v_min).to(torch.float32) + self.mem = self.mem-min_reset*(self.mem-self.v_rest) - self.spk = self.fire(self.mem) - - do_spike_reset = (self.spk/self.graded_spikes_factor - self.reset) - do_min_reset = (self.mem<self.v_min).to(torch.float32) + if self.quantization_flag: + self.mem.data = QuantizeSTE.apply(self.mem, self.quantizer) - self.mem = self.mem - do_spike_reset*(self.mem-self.v_rest) - self.mem = self.mem - do_min_reset*(self.mem-self.v_rest) + spk = self.fire(self.mem) - return self.spk, self.mem + return spk, self.mem def get_builder_module(self, module_name : str, output_path : str = "."): """ @@ -335,56 +318,18 @@ class RSLIF(LIF, ModNEFNeuron): output_path=output_path ) return module - - def quantize_weight(self): - """ - Quantize synaptic weight - """ - - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(weight=self.fc.weight, rec_weight=self.reccurent.weight) - self.fc.weight.data = self.quantizer(self.fc.weight.data, True) - self.reccurent.weight.data = self.quantizer(self.reccurent.weight.data, True) - - def quantize_parameters(self): + def quantize_hp(self): """ - Quantize neuron hyper-parameters + neuron hyper-parameters quantization + We assume you've already intialize quantizer """ - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(weight=self.fc.weight, rec_weight=self.reccurent.weight) - - self.v_leak.data = self.quantizer(self.v_leak.data, True) - self.v_min.data = self.quantizer(self.v_min.data, True) - self.v_rest.data = self.quantizer(self.v_rest.data, True) - self.threshold.data = self.quantizer(self.threshold, True) + self.v_leak.data = QuantizeSTE.apply(self.v_leak, self.quantizer) + self.v_min.data = QuantizeSTE.apply(self.v_min, self.quantizer) + self.v_rest.data = QuantizeSTE.apply(self.v_rest, self.quantizer) + self.threshold.data = QuantizeSTE.apply(self.threshold, self.quantizer) - def quantize(self, force_init=False): - """ - Quantize synaptic weight and neuron hyper-parameters - - Parameters - ---------- - force_init = Fasle : bool - force quantizer initialization - """ - - if force_init or not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight, self.reccurent.weight) - - self.quantize_weight() - self.quantize_parameters() - - def clamp(self): - """ - Clamp synaptic weight and neuron hyper-parameters - """ - - self.fc.weight.data = self.quantizer.clamp(self.fc.weight.data) - self.reccurent.weight.data = self.quantizer.clamp(self.reccurent.weight.data) - - @classmethod def detach_hidden(cls): """Returns the hidden states, detached from the current graph. diff --git a/modneflib/modnef/modnef_torch/modnef_neurons/slif_model/slif.py b/modneflib/modnef/modnef_torch/modnef_neurons/slif_model/slif.py index fa370b99a19bbe8ea30bf26807b506bcddfa1e44..5e93c3926028c2c2799e27dcf0a2e148b28cf33a 100644 --- a/modneflib/modnef/modnef_torch/modnef_neurons/slif_model/slif.py +++ b/modneflib/modnef/modnef_torch/modnef_neurons/slif_model/slif.py @@ -1,7 +1,7 @@ """ File name: slif Author: Aurélie Saulquin -Version: 1.1.0 +Version: 1.2.1 License: GPL-3.0-or-later Contact: aurelie.saulquin@univ-lille.fr Dependencies: torch, snntorch, modnef.archbuilder, modnef_torch_neuron, math, modnef.quantizer @@ -9,16 +9,14 @@ Descriptions: ModNEF torch SLIF neuron model Based on snntorch.Leaky and snntroch.LIF class """ -import torch.nn as nn import torch -from snntorch import LIF import modnef.arch_builder as builder from modnef.arch_builder.modules.utilities import * from ..modnef_torch_neuron import ModNEFNeuron, _quantizer from math import ceil, log -from modnef.quantizer import MinMaxQuantizer +from modnef.quantizer import MinMaxQuantizer, QuantizeSTE -class SLIF(LIF, ModNEFNeuron): +class SLIF(ModNEFNeuron): """ ModNEFTorch reccurent Simplifed LIF neuron model @@ -119,34 +117,18 @@ class SLIF(LIF, ModNEFNeuron): quantization method """ - LIF.__init__( - self=self, - beta = v_leak, - threshold=threshold, - spike_grad=spike_grad, - surrogate_disable=False, - init_hidden=False, - inhibition=False, - learn_beta=False, - learn_threshold=False, - reset_mechanism="zero", - state_quant=False, - output=False, - graded_spikes_factor=1.0, - learn_graded_spikes_factor=False - ) - - ModNEFNeuron.__init__(self, quantizer=quantizer) + super().__init__(threshold=threshold, + in_features=in_features, + out_features=out_features, + reset_mechanism="zero", + spike_grad=spike_grad, + quantizer=quantizer + ) self.register_buffer("v_leak", torch.as_tensor(v_leak)) self.register_buffer("v_min", torch.as_tensor(v_min)) self.register_buffer("v_rest", torch.as_tensor(v_rest)) - self.in_features = in_features - self.out_features = out_features - - self.fc = nn.Linear(self.in_features, self.out_features, bias=False) - self._init_mem() self.hardware_description = { @@ -251,32 +233,33 @@ class SLIF(LIF, ModNEFNeuron): if not spk == None: self.spk = spk - input_ = self.fc(input_) + quant = self.quantizer if self.quantization_flag else None - if not self.mem.shape == input_.shape: - self.mem = torch.ones_like(input_)*self.v_rest - - if self.quantization_flag: - input_.data = self.quantizer(input_.data, True) - self.mem.data = self.quantizer(self.mem.data, True) + forward_current = self.fc(input_, quant) + if not self.mem.shape == forward_current.shape: + self.mem = torch.ones_like(forward_current, device=self.mem.device)*self.v_rest + + self.reset = self.mem_reset(self.mem) - self.mem = self.mem + input_ + self.mem = self.mem + forward_current + + self.mem = self.mem - self.reset*(self.mem-self.v_rest) if self.hardware_estimation_flag: - self.val_min = torch.min(torch.min(input_.min(), self.mem.min()), self.val_min) - self.val_max = torch.max(torch.max(input_.max(), self.mem.max()), self.val_max) - - self.mem = self.mem-self.v_leak + self.val_min = torch.min(self.mem.min(), self.val_min).detach() + self.val_max = torch.max(self.mem.max(), self.val_max).detach() - spk = self.fire(self.mem) + # update neuron + self.mem = self.mem - self.v_leak + min_reset = (self.mem<self.v_min).to(torch.float32) + self.mem = self.mem-min_reset*(self.mem-self.v_rest) - do_spike_reset = (spk/self.graded_spikes_factor - self.reset) - do_min_reset = (self.mem<self.v_min).to(torch.float32) + if self.quantization_flag: + self.mem.data = QuantizeSTE.apply(self.mem, self.quantizer) - self.mem = self.mem - do_spike_reset*(self.mem-self.v_rest) - self.mem = self.mem - do_min_reset*(self.mem-self.v_rest) + spk = self.fire(self.mem) return spk, self.mem @@ -299,9 +282,7 @@ class SLIF(LIF, ModNEFNeuron): if self.hardware_description["variable_size"]==-1: if self.hardware_estimation_flag: val_max = max(abs(self.val_max), abs(self.val_min)) - print(val_max) - val_max = self.quantizer(val_max, dtype=torch.int32) - print(val_max) + val_max = self.quantizer(val_max) self.hardware_description["variable_size"] = ceil(log(val_max)/log(256))*8 else: self.hardware_description["variable_size"]=16 @@ -326,53 +307,18 @@ class SLIF(LIF, ModNEFNeuron): output_path=output_path ) return module - - def quantize_weight(self): - """ - Quantize synaptic weight - """ - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(weight=self.fc.weight) - - self.fc.weight.data = self.quantizer(self.fc.weight.data, True) - - def quantize_parameters(self): + def quantize_hp(self): """ - Quantize neuron hyper-parameters + neuron hyper-parameters quantization + We assume you've already intialize quantizer """ - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(weight=self.fc.weight) + self.v_leak.data = QuantizeSTE.apply(self.v_leak, self.quantizer) + self.v_min.data = QuantizeSTE.apply(self.v_min, self.quantizer) + self.v_rest.data = QuantizeSTE.apply(self.v_rest, self.quantizer) + self.threshold.data = QuantizeSTE.apply(self.threshold, self.quantizer) - self.v_leak.data = self.quantizer(self.v_leak.data, True) - self.v_min.data = self.quantizer(self.v_min.data, True) - self.v_rest.data = self.quantizer(self.v_rest.data, True) - self.threshold.data = self.quantizer(self.threshold, True) - - def quantize(self, force_init=False): - """ - Quantize synaptic weight and neuron hyper-parameters - - Parameters - ---------- - force_init = Fasle : bool - force quantizer initialization - """ - - if force_init or not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight) - - self.quantize_weight() - self.quantize_parameters() - - def clamp(self): - """ - Clamp synaptic weight and neuron hyper-parameters - """ - - self.fc.weight.data = self.quantizer.clamp(self.fc.weight.data) - @classmethod def detach_hidden(cls): """Returns the hidden states, detached from the current graph. diff --git a/modneflib/modnef/modnef_torch/modnef_neurons/srlif_model/rshiftlif.py b/modneflib/modnef/modnef_torch/modnef_neurons/srlif_model/rshiftlif.py index 0193876ac1a13774dcc1062ba9642cacffdfbec3..07a631bc275e75bdc04896a02f0a04cc9f39d41f 100644 --- a/modneflib/modnef/modnef_torch/modnef_neurons/srlif_model/rshiftlif.py +++ b/modneflib/modnef/modnef_torch/modnef_neurons/srlif_model/rshiftlif.py @@ -1,7 +1,7 @@ """ File name: rsrlif Author: Aurélie Saulquin -Version: 1.1.0 +Version: 1.2.1 License: GPL-3.0-or-later Contact: aurelie.saulquin@univ-lille.fr Dependencies: torch, snntorch, modnef.archbuilder, modnef_torch_neuron, math, modnef.quantizer @@ -9,17 +9,16 @@ Descriptions: ModNEF torch reccurent Shift LIF neuron model Based on snntorch.RLeaky and snntorch.LIF class """ -import torch.nn as nn import torch -from snntorch import LIF import modnef.arch_builder as builder from modnef.arch_builder.modules.utilities import * from ..modnef_torch_neuron import ModNEFNeuron, _quantizer +from modnef.modnef_torch.quantLinear import QuantLinear from math import log, ceil -from modnef.quantizer import DynamicScaleFactorQuantizer +from modnef.quantizer import DynamicScaleFactorQuantizer, QuantizeSTE -class RShiftLIF(LIF, ModNEFNeuron): +class RShiftLIF(ModNEFNeuron): """ ModNEFTorch reccurent Shift LIF neuron model @@ -116,34 +115,24 @@ class RShiftLIF(LIF, ModNEFNeuron): quantizer = DynamicScaleFactoirQuantizer(8) : Quantizer quantization method """ + + super().__init__(threshold=threshold, + in_features=in_features, + out_features=out_features, + reset_mechanism=reset_mechanism, + spike_grad=spike_grad, + quantizer=quantizer + ) self.shift = int(-log(1-beta)/log(2)) if (1-2**-self.shift) != beta: print(f"initial value of beta ({beta}) has been change for {1-2**-self.shift} = 1-2**-{self.shift}") beta = 1-2**-self.shift - - LIF.__init__( - self=self, - beta=beta, - threshold=threshold, - spike_grad=spike_grad, - surrogate_disable=False, - init_hidden=False, - inhibition=False, - learn_beta=False, - learn_threshold=False, - reset_mechanism=reset_mechanism, - state_quant=False, - output=False, - graded_spikes_factor=1.0, - learn_graded_spikes_factor=False, - ) - ModNEFNeuron.__init__(self=self, quantizer=quantizer) + self.reccurent = QuantLinear(out_features, out_features) - self.fc = nn.Linear(in_features, out_features, bias=False) - self.reccurent = nn.Linear(out_features, out_features, bias=False) + self.register_buffer("beta", torch.tensor(beta)) self._init_mem() @@ -256,36 +245,35 @@ class RShiftLIF(LIF, ModNEFNeuron): if not spk == None: self.spk = spk - input_ = self.fc(input_) - - if not self.mem.shape == input_.shape: - self.mem = torch.zeros_like(input_, device=self.mem.device) + quant = self.quantizer if self.quantization_flag else None - if not self.spk.shape == input_.shape: - self.spk = torch.zeros_like(input_, device=self.spk.device) + forward_current = self.fc(input_, quant) - self.reset = self.mem_reset(self.mem) - - rec_input = self.reccurent(self.spk) + if not self.mem.shape == forward_current.shape: + self.mem = torch.zeros_like(forward_current, device=self.mem.device) + + if not self.spk.shape == forward_current.shape: + self.spk = torch.zeros_like(forward_current, device=self.spk.device) - if self.quantization_flag: - self.mem.data = self.quantizer(self.mem.data, True) - input_.data = self.quantizer(input_.data, True) - rec_input.data = self.quantizer(rec_input.data, True) + rec_current = self.reccurent(self.spk, quant) + self.reset = self.mem_reset(self.mem) - self.mem = self.mem+input_+rec_input + self.mem = self.mem+forward_current+rec_current if self.reset_mechanism == "subtract": - self.mem = self.mem-self.__shift(self.mem)-self.reset*self.threshold + self.mem = self.mem-self.reset*self.threshold elif self.reset_mechanism == "zero": - self.mem = self.mem-self.__shift(self.mem)-self.reset*self.mem - else: - self.mem = self.mem*self.beta + self.mem = self.mem-self.reset*self.mem if self.hardware_estimation_flag: - self.val_min = torch.min(torch.min(input_.min(), self.mem.min()), self.val_min) - self.val_max = torch.max(torch.max(input_.max(), self.mem.max()), self.val_max) + self.val_min = torch.min(self.mem.min(), self.val_min).detach() + self.val_max = torch.max(self.mem.max(), self.val_max).detach() + + self.mem = self.mem-self.__shift(self.mem) + + if self.quantization_flag: + self.mem.data = QuantizeSTE.apply(self.mem, self.quantizer) self.spk = self.fire(self.mem) @@ -310,7 +298,7 @@ class RShiftLIF(LIF, ModNEFNeuron): if self.hardware_description["variable_size"]==-1: if self.hardware_estimation_flag: val_max = max(abs(self.val_max), abs(self.val_min)) - val_max = val_max*2**(self.hardware_description["compute_fp"]) + val_max = self.quantizer(self.val_max) self.hardware_description["variable_size"] = ceil(log(val_max)/log(256))*8 else: self.hardware_description["variable_size"]=16 @@ -337,51 +325,13 @@ class RShiftLIF(LIF, ModNEFNeuron): ) return module - def quantize_weight(self): - """ - Quantize synaptic weight - """ - - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(weight=self.fc.weight, rec_weight=self.reccurent.weight) - - self.fc.weight.data = self.quantizer(self.fc.weight.data, True) - self.reccurent.weight.data = self.quantizer(self.reccurent.weight.data, True) - - def quantize_parameters(self): - """ - Quantize neuron hyper-parameters - """ - - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(weight=self.fc.weight, rec_weight=self.reccurent.weight) - - self.threshold.data = self.quantizer(self.threshold.data, True) - self.beta.data = self.quantizer(self.beta.data, True) - - def quantize(self, force_init=False): - """ - Quantize synaptic weight and neuron hyper-parameters - - Parameters - ---------- - force_init = Fasle : bool - force quantizer initialization - """ - - if force_init or not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight, self.reccurent.weight) - - self.quantize_weight() - self.quantize_parameters() - - def clamp(self): + def quantize_hp(self): """ - Clamp synaptic weight and neuron hyper-parameters + neuron hyper-parameters quantization. + We assume you already initialize quantizer """ - self.fc.weight.data = self.quantizer.clamp(self.fc.weight.data) - self.reccurent.weight.data = self.quantizer.clamp(self.reccurent.weight.data) + self.threshold.data = QuantizeSTE.apply(self.threshold, self.quantizer) @classmethod diff --git a/modneflib/modnef/modnef_torch/modnef_neurons/srlif_model/shiftlif.py b/modneflib/modnef/modnef_torch/modnef_neurons/srlif_model/shiftlif.py index 9bb6d1df8eeff1ab9044378d73e3625e4a9e4438..92698f819dd7b2e07dc12e64893015a40af2716d 100644 --- a/modneflib/modnef/modnef_torch/modnef_neurons/srlif_model/shiftlif.py +++ b/modneflib/modnef/modnef_torch/modnef_neurons/srlif_model/shiftlif.py @@ -1,7 +1,7 @@ """ File name: srlif Author: Aurélie Saulquin -Version: 1.1.0 +Version: 1.2.1 License: GPL-3.0-or-later Contact: aurelie.saulquin@univ-lille.fr Dependencies: torch, snntorch, modnef.archbuilder, modnef_torch_neuron, math, modnef.quantizer @@ -9,16 +9,17 @@ Descriptions: ModNEF torch Shift LIF neuron model Based on snntorch.Leaky and snntroch.LIF class """ -import torch.nn as nn import torch -from snntorch import LIF +from snntorch.surrogate import fast_sigmoid import modnef.arch_builder as builder from modnef.arch_builder.modules.utilities import * from ..modnef_torch_neuron import ModNEFNeuron, _quantizer from math import log, ceil -from modnef.quantizer import DynamicScaleFactorQuantizer +from modnef.quantizer import DynamicScaleFactorQuantizer, QuantizeSTE -class ShiftLIF(LIF, ModNEFNeuron): + + +class ShiftLIF(ModNEFNeuron): """ ModNEFTorch Shift LIF neuron model @@ -86,7 +87,7 @@ class ShiftLIF(LIF, ModNEFNeuron): out_features, beta, threshold=1.0, - spike_grad=None, + spike_grad=fast_sigmoid(slope=25), reset_mechanism="subtract", quantizer=DynamicScaleFactorQuantizer(8) ): @@ -110,6 +111,17 @@ class ShiftLIF(LIF, ModNEFNeuron): quantizer = DynamicScaleFactorQuantizer(8) : Quantizer quantization method """ + + super().__init__(threshold=threshold, + in_features=in_features, + out_features=out_features, + reset_mechanism=reset_mechanism, + spike_grad=spike_grad, + quantizer=quantizer + ) + + + self.shift = int(-log(1-beta)/log(2)) @@ -117,26 +129,8 @@ class ShiftLIF(LIF, ModNEFNeuron): print(f"initial value of beta ({beta}) has been change for {1-2**-self.shift} = 1-2**-{self.shift}") beta = 1-2**-self.shift - LIF.__init__( - self=self, - beta=beta, - threshold=threshold, - spike_grad=spike_grad, - surrogate_disable=False, - init_hidden=False, - inhibition=False, - learn_beta=False, - learn_threshold=False, - reset_mechanism=reset_mechanism, - state_quant=False, - output=False, - graded_spikes_factor=1.0, - learn_graded_spikes_factor=False, - ) - ModNEFNeuron.__init__(self=self, quantizer=quantizer) - - self.fc = nn.Linear(in_features, out_features, bias=False) + self.register_buffer("beta", torch.tensor(beta)) self._init_mem() @@ -249,29 +243,30 @@ class ShiftLIF(LIF, ModNEFNeuron): if not spk == None: self.spk = spk - input_ = self.fc(input_) + quant = self.quantizer if self.quantization_flag else None - if not self.mem.shape == input_.shape: - self.mem = torch.zeros_like(input_, device=self.mem.device) + forward_current = self.fc(input_, quant) - self.reset = self.mem_reset(self.mem) + if not self.mem.shape == forward_current.shape: + self.mem = torch.zeros_like(forward_current, device=self.mem.device) - if self.quantization_flag: - self.mem.data = self.quantizer(self.mem.data, True) - input_.data = self.quantizer(input_.data, True) + self.reset = self.mem_reset(self.mem) - self.mem = self.mem+input_ + self.mem = self.mem+forward_current if self.reset_mechanism == "subtract": - self.mem = self.mem-self.__shift(self.mem)-self.reset*self.threshold + self.mem = self.mem-self.reset*self.threshold elif self.reset_mechanism == "zero": - self.mem = self.mem-self.__shift(self.mem)-self.reset*self.mem - else: - self.mem = self.mem-self.__shift(self.mem) + self.mem = self.mem-self.reset*self.mem if self.hardware_estimation_flag: - self.val_min = torch.min(torch.min(input_.min(), self.mem.min()), self.val_min) - self.val_max = torch.max(torch.max(input_.max(), self.mem.max()), self.val_max) + self.val_min = torch.min(self.mem.min(), self.val_min).detach() + self.val_max = torch.max(self.mem.max(), self.val_max).detach() + + self.mem = self.mem-self.__shift(self.mem) + + if self.quantization_flag: + self.mem.data = QuantizeSTE.apply(self.mem, self.quantizer) self.spk = self.fire(self.mem) @@ -301,6 +296,8 @@ class ShiftLIF(LIF, ModNEFNeuron): else: self.hardware_description["variable_size"]=16 + #self.clamp(force_init=True) + module = builder.ShiftLif( name=module_name, input_neuron=self.fc.in_features, @@ -321,48 +318,13 @@ class ShiftLIF(LIF, ModNEFNeuron): ) return module - def quantize_weight(self): - """ - Quantize synaptic weight - """ - - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(weight=self.fc.weight) - - self.fc.weight.data = self.quantizer(self.fc.weight.data, True) - - def quantize_parameters(self): - """ - Quantize neuron hyper-parameters - """ - - if not self.quantizer.is_initialize: - self.quantizer.init_from_weight(weight=self.fc.weight) - - self.threshold.data = self.quantizer(self.threshold.data, True) - - def quantize(self, force_init=False): - """ - Quantize synaptic weight and neuron hyper-parameters - - Parameters - ---------- - force_init = Fasle : bool - force quantizer initialization - """ - - if force_init or not self.quantizer.is_initialize: - self.quantizer.init_from_weight(self.fc.weight) - - self.quantize_weight() - self.quantize_parameters() - - def clamp(self): + def quantize_hp(self): """ - Clamp synaptic weight and neuron hyper-parameters + neuron hyper-parameters quantization. + We assume you already initialize quantizer """ - self.fc.weight.data = self.quantizer.clamp(self.fc.weight.data) + self.threshold.data = QuantizeSTE.apply(self.threshold, self.quantizer) @classmethod diff --git a/modneflib/modnef/modnef_torch/quantLinear.py b/modneflib/modnef/modnef_torch/quantLinear.py new file mode 100644 index 0000000000000000000000000000000000000000..f22f0a520133df3baa9723a1db5dc1311fbeaa61 --- /dev/null +++ b/modneflib/modnef/modnef_torch/quantLinear.py @@ -0,0 +1,59 @@ +""" +File name: quantLinear +Author: Aurélie Saulquin +Version: 0.1.1 +License: GPL-3.0-or-later +Contact: aurelie.saulquin@univ-lille.fr +Dependencies: torch, modnef.quantizer +Descriptions: Quantized Linear torch layer +""" + +import torch.nn as nn +from modnef.quantizer import QuantizeSTE + +class QuantLinear(nn.Linear): + """ + Quantized Linear torch layer + Extended from torch.nn.Linear + + Methods + ------- + forward(x, quantizer=None) + Apply linear forward, if quantizer!=None, quantized weight are used for linear + """ + + def __init__(self, in_features : int, out_features : int): + """ + Initialize class + + Parameters + ---------- + in_features : int + input features of layer + out_features : int + output features of layer + """ + + super().__init__(in_features=in_features, out_features=out_features, bias=False) + + def forward(self, x, quantizer=None): + """ + Apply linear forward, if quantizer!=None, quantized weight are used for linear + + Parameters + ---------- + x : Torch + input spikes + quantizer = None : Quantizer + quantization method. + If None, full precision weight are used for linear + """ + + if quantizer!=None: + w = QuantizeSTE.apply(self.weight, quantizer) + #w.data = quantizer.clamp(w) + else: + w = self.weight + + + return nn.functional.linear(x, w) \ No newline at end of file diff --git a/modneflib/modnef/quantizer/__init__.py b/modneflib/modnef/quantizer/__init__.py index 2455aa927340a4e69eb2cdd0f63a8b2e10270107..7ca95c0dde63c9d819a40ee3cd54da799384ebe1 100644 --- a/modneflib/modnef/quantizer/__init__.py +++ b/modneflib/modnef/quantizer/__init__.py @@ -10,4 +10,6 @@ Descriptions: ModNEF quantizer method from .quantizer import Quantizer from .fixed_point_quantizer import FixedPointQuantizer from .min_max_quantizer import MinMaxQuantizer -from .dynamic_scale_quantizer import DynamicScaleFactorQuantizer \ No newline at end of file +from .dynamic_scale_quantizer import DynamicScaleFactorQuantizer +from .ste_quantizer import QuantizeSTE +from .quantizer_scheduler import QuantizerScheduler \ No newline at end of file diff --git a/modneflib/modnef/quantizer/dynamic_scale_quantizer.py b/modneflib/modnef/quantizer/dynamic_scale_quantizer.py index 724a8f4b739bc34c14ea691dd19cfc8df795bcba..f1d7497a8893891ea15c8d2446f4a2b512848b1c 100644 --- a/modneflib/modnef/quantizer/dynamic_scale_quantizer.py +++ b/modneflib/modnef/quantizer/dynamic_scale_quantizer.py @@ -110,11 +110,11 @@ class DynamicScaleFactorQuantizer(Quantizer): self.is_initialize = True - if torch.is_tensor(weight): + if not torch.is_tensor(weight): weight = torch.Tensor(weight) - if torch.is_tensor(rec_weight): - rec_weight = torch.Tensor(weight) + if not torch.is_tensor(rec_weight): + rec_weight = torch.Tensor(rec_weight) if self.signed==None: self.signed = torch.min(weight.min(), rec_weight.min())<0.0 @@ -130,7 +130,7 @@ class DynamicScaleFactorQuantizer(Quantizer): #self.scale_factor = torch.max(torch.abs(weight).max(), torch.abs(weight).max())/2**(self.bitwidth-1) - def _quant(self, data, unscale) -> torch.Tensor: + def _quant(self, data) -> torch.Tensor: """ Apply quantization @@ -138,8 +138,6 @@ class DynamicScaleFactorQuantizer(Quantizer): ---------- data : Tensor data to quantize - unscale = False : bool - If true, apply quantization and then, unquantize data to simulate quantization Returns ------- @@ -147,8 +145,20 @@ class DynamicScaleFactorQuantizer(Quantizer): """ scaled = torch.round(data/self.scale_factor).to(self.dtype) + + return scaled + + def _unquant(self, data) -> torch.Tensor: + """ + Unquantize data - if unscale: - return scaled*self.scale_factor - else: - return scaled \ No newline at end of file + Parameters + ---------- + data : Tensor + data to unquatize + + Returns + ------- + Tensor + """ + return data*self.scale_factor \ No newline at end of file diff --git a/modneflib/modnef/quantizer/fixed_point_quantizer.py b/modneflib/modnef/quantizer/fixed_point_quantizer.py index de6c789ff486c8df6c7abb86325d8c458086028d..16b08e53490ba071295f0c960eac0fc53c44335d 100644 --- a/modneflib/modnef/quantizer/fixed_point_quantizer.py +++ b/modneflib/modnef/quantizer/fixed_point_quantizer.py @@ -67,9 +67,6 @@ class FixedPointQuantizer(Quantizer): dtype = torch.int32 : torch.dtype type use during conversion """ - - if bitwidth==-1 and fixed_point==-1: - raise Exception("You must fix at least one value to compute the other one") super().__init__( bitwidth=bitwidth, @@ -145,9 +142,9 @@ class FixedPointQuantizer(Quantizer): elif self.fixed_point==-1: self.fixed_point = self.bitwidth-int_part_size self.scale_factor = 2**self.fixed_point + - - def _quant(self, data, unscale) -> torch.Tensor: + def _quant(self, data) -> torch.Tensor: """ Apply quantization @@ -155,8 +152,6 @@ class FixedPointQuantizer(Quantizer): ---------- data : Tensor data to quantize - unscale = False : bool - If true, apply quantization and then, unquantize data to simulate quantization Returns ------- @@ -167,13 +162,20 @@ class FixedPointQuantizer(Quantizer): #scaled = torch.clamp(scaled, -2**(self.bitwidth-1), 2**(self.bitwidth-1)-1) - if unscale: - return (scaled.to(torch.float32))/self.scale_factor - else: - return scaled + return scaled - def _clamp(self, data): - b_min = (-2**(self.bitwidth-int(self.signed))*int(self.signed))/self.scale_factor - b_max = (2**(self.bitwidth-int(self.signed))-1)/self.scale_factor + def _unquant(self, data) -> torch.Tensor: + """ + Unquantize data + + Parameters + ---------- + data : Tensor + data to unquatize + + Returns + ------- + Tensor + """ - return torch.clamp(data, min=b_min, max=b_max) \ No newline at end of file + return (data.to(torch.float32))/self.scale_factor \ No newline at end of file diff --git a/modneflib/modnef/quantizer/min_max_quantizer.py b/modneflib/modnef/quantizer/min_max_quantizer.py index a700f6bf2e99bda84af2f530e925af551cdf1c10..6ca813180dfc9d0de66ff4428564c6240b9f4649 100644 --- a/modneflib/modnef/quantizer/min_max_quantizer.py +++ b/modneflib/modnef/quantizer/min_max_quantizer.py @@ -134,7 +134,7 @@ class MinMaxQuantizer(Quantizer): self.b_max = 2**(self.bitwidth-int(self.signed))-1 self.b_min = -int(self.signed)*self.b_max - def _quant(self, data, unscale) -> torch.Tensor: + def _quant(self, data) -> torch.Tensor: """ Apply quantization @@ -142,8 +142,6 @@ class MinMaxQuantizer(Quantizer): ---------- data : Tensor data to quantize - unscale = False : bool - If true, apply quantization and then, unquantize data to simulate quantization Returns ------- @@ -152,7 +150,20 @@ class MinMaxQuantizer(Quantizer): scaled = ((data-self.x_min)/(self.x_max-self.x_min)*(self.b_max-self.b_min)+self.b_min).to(self.dtype) - if unscale: - return (scaled-self.b_min)/(self.b_max-self.b_min)*(self.x_max-self.x_min)+self.x_min - else: - return scaled \ No newline at end of file + return scaled + + def _unquant(self, data) -> torch.Tensor: + """ + Unquantize data + + Parameters + ---------- + data : Tensor + data to unquatize + + Returns + ------- + Tensor + """ + + return (data-self.b_min)/(self.b_max-self.b_min)*(self.x_max-self.x_min)+self.x_min \ No newline at end of file diff --git a/modneflib/modnef/quantizer/quantizer.py b/modneflib/modnef/quantizer/quantizer.py index 235981d76cf33be6881d9f6d16181aa68fc76c5e..20914b619575f2200ecf21dcb152e902eef42c85 100644 --- a/modneflib/modnef/quantizer/quantizer.py +++ b/modneflib/modnef/quantizer/quantizer.py @@ -91,7 +91,7 @@ class Quantizer(): raise NotImplementedError() - def __call__(self, data, unscale=False): + def __call__(self, data, unscale=False, clamp=False): """ Call quantization function @@ -106,19 +106,34 @@ class Quantizer(): ------- int | float | list | numpy.array | Tensor (depending on type of data) """ + + if not torch.is_tensor(data): + tdata = torch.tensor(data, dtype=torch.float32) + else: + tdata = data + + qdata = self._quant(tdata) + + if clamp: + born_min = torch.tensor(-int(self.signed)*2**(self.bitwidth-1)).to(qdata.device) + born_max = torch.tensor(2**(self.bitwidth-int(self.signed))-1).to(qdata.device) + qdata = torch.clamp(qdata, min=born_min, max=born_max) + + if unscale: + qdata = self._unquant(qdata).to(torch.float32) if isinstance(data, (int, float)): - return self._quant(data=torch.tensor(data), unscale=unscale).item() + return qdata.item() elif isinstance(data, list): - return self._quant(data=torch.tensor(data), unscale=unscale).tolist() + return qdata.tolist() elif isinstance(data, np.ndarray): - return self._quant(data=torch.tensor(data), unscale=unscale).numpy() + return qdata.numpy() elif torch.is_tensor(data): - return self._quant(data=data, unscale=unscale).detach() + return qdata.detach() else: raise TypeError("Unsupported data type") - def _quant(self, data, unscale) -> torch.Tensor: + def _quant(self, data) -> torch.Tensor: """ Apply quantization @@ -126,34 +141,46 @@ class Quantizer(): ---------- data : Tensor data to quantize - unscale = False : bool - If true, apply quantization and then, unquantize data to simulate quantization Returns ------- Tensor """ - pass + raise NotImplementedError() + + def _unquant(self, data) -> torch.Tensor: + """ + Unquantize data + + Parameters + ---------- + data : Tensor + data to unquatize + + Returns + ------- + Tensor + """ + + raise NotImplementedError() def clamp(self, data): """ - Call quantization function + Call clamp function Parameters ---------- data : int | float | list | numpy.array | Tensor data to quantize - unscale = False : bool - If true, apply quantization and then, unquantize data to simulate quantization Returns ------- int | float | list | numpy.array | Tensor (depending on type of data) """ - born_min = -int(self.signed)*2**(self.bitwidth-1) - born_max = 2**(self.bitwidth-int(self.signed))-1 + born_min = self._unquant(torch.tensor(-int(self.signed)*2**(self.bitwidth-1))).item() + born_max = self._unquant(torch.tensor(2**(self.bitwidth-int(self.signed))-1)).item() if isinstance(data, (int, float)): return torch.clamp(torch.tensor(data), min=born_min, max=born_max).item() diff --git a/modneflib/modnef/quantizer/quantizer_scheduler.py b/modneflib/modnef/quantizer/quantizer_scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..2d51f186aabc5136fe00984dfee9ff6938a084f6 --- /dev/null +++ b/modneflib/modnef/quantizer/quantizer_scheduler.py @@ -0,0 +1,55 @@ +""" +File name: quantizer_scheduler +Author: Aurélie Saulquin +Version: 0.1.0 +License: GPL-3.0-or-later +Contact: aurelie.saulquin@univ-lille.fr +Dependencies: modnef.modnef_torch +Descriptions: ModNEF quantizer scheduler +""" + +from modnef.modnef_torch import ModNEFNeuron + +class QuantizerScheduler(): + + def __init__(self, model, bit_range, T, quantizationMethod): + + self.num_bits = [i for i in range(bit_range[0], bit_range[1]-1, -1)] + + self.bit_counter = 0 + self.epoch_counter = 0 + + self.bitwidth = self.num_bits[self.bit_counter] + + self.period = T + self.epoch_max = self.period*(len(self.num_bits)-1) + + self.model = model + + self.quantizationMethod = quantizationMethod + + self.__update() + + + + def __update(self): + for m in self.model.modules(): + if isinstance(m, ModNEFNeuron): + m.quantizer = self.quantizationMethod(self.bitwidth) + m.init_quantizer() + m.quantize_hp() + + def step(self): + + self.epoch_counter += 1 + + if self.epoch_counter > self.epoch_max: + return + else: + if self.epoch_counter%self.period==0: + self.bit_counter += 1 + self.bitwidth = self.num_bits[self.bit_counter] + self.__update() + + def save_model(self): + return self.epoch_counter >= self.epoch_max diff --git a/modneflib/modnef/quantizer/ste_quantizer.py b/modneflib/modnef/quantizer/ste_quantizer.py new file mode 100644 index 0000000000000000000000000000000000000000..b66832e320fbb464ae48abcb65b866b9ace0d662 --- /dev/null +++ b/modneflib/modnef/quantizer/ste_quantizer.py @@ -0,0 +1,62 @@ +""" +File name: ste_quantizer +Author: Aurélie Saulquin +Version: 0.1.0 +License: GPL-3.0-or-later +Contact: aurelie.saulquin@univ-lille.fr +Dependencies: torch +Descriptions: Straight-Throught Estimator quantization method +""" + +import torch + +class QuantizeSTE(torch.autograd.Function): + """ + Straight-Throught Estimator quantization method + + Methods + ------- + @staticmethod + forward(ctx, data, quantizer) + Apply quantization method to data + @staticmethod + backward(ctx, grad_output) + Returns backward gradient + """ + + @staticmethod + def forward(ctx, data, quantizer, clamp=False): + """ + Apply quantization method to data + + Parameters + ---------- + ctx : torch.autograd.function.BackwardCFunction + Autograd context used to store variables for the backward pass + data : Tensor + data to quantize + quantizer : Quantizer + quantization method applied to data + """ + + q_data = quantizer(data, unscale=True, clamp=clamp) + + + ctx.scale = quantizer.scale_factor + + return q_data + + @staticmethod + def backward(ctx, grad_output): + """ + Return backward gradient without modificiation + + Parameters + ---------- + ctx : torch.autograd.function.BackwardCFunction + Autograd context used to store variables for the backward pass + grad_output : Tensor + gradient + """ + + return grad_output, None \ No newline at end of file diff --git a/modneflib/modnef/templates/dataset.py b/modneflib/modnef/templates/dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..e0b86fc5a43069f74cf733e33b7dd16c7d8360af --- /dev/null +++ b/modneflib/modnef/templates/dataset.py @@ -0,0 +1,20 @@ +import os +import tonic +from torch.utils.data import DataLoader + +"""DataSet Definition""" +dataset_path = f"{os.environ['HOME']}/datasets" + +# data set definition, change to your dataset +sensor_size = tonic.datasets.NMNIST.sensor_size +frame_transform = tonic.transforms.ToFrame(sensor_size=sensor_size, n_time_bins=5) + +train_set = tonic.datasets.NMNIST(save_to=dataset_path, train=True, transform=frame_transform) +test_set = tonic.datasets.NMNIST(save_to=dataset_path, train=False, transform=frame_transform) + +# batch loader +batch_size = 64 + +trainLoader = DataLoader(train_set, batch_size=batch_size, shuffle=True, drop_last = True, collate_fn = tonic.collation.PadTensors(batch_first=True)) +testLoader = DataLoader(test_set, batch_size=batch_size, shuffle=True, drop_last = True, collate_fn = tonic.collation.PadTensors(batch_first=True)) +validationLoader = None \ No newline at end of file diff --git a/modneflib/modnef/templates/evaluation.py b/modneflib/modnef/templates/evaluation.py index aa74e22091b28a52bb36892ac081df0c918c7803..8fa94db51748bc8254de854504312ab584edd0ad 100644 --- a/modneflib/modnef/templates/evaluation.py +++ b/modneflib/modnef/templates/evaluation.py @@ -6,19 +6,26 @@ from snntorch.surrogate import fast_sigmoid import torch from run_lib import * import sys +from model import MyModel +from dataset import * if __name__ == "__main__": """Experience name""" exp_name = "Evaluation" + """Device definition""" + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + """Model definition""" - model_path = "model_template.json" best_model_name = "best_model" - model = ModNEFModelBuilder(model_path, spike_grad=fast_sigmoid(slope=25)) + # model_path = "model_template.json" + # model = ModNEFModelBuilder(model_path, spike_grad=fast_sigmoid(slope=25)) + + model = MyModel("template_model", spike_grad=fast_sigmoid(slope=25)) - model.load_state_dict(torch.load(best_model_name)) + model.load_state_dict(torch.load(best_model_name, map_location=device)) """Kind of run @@ -29,16 +36,12 @@ if __name__ == "__main__": kind = sys.argv[1] - """Device definition""" - device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") - """Evaluation variable definition""" verbose = True - save_conf_matrix = False output_path = "." """FPGA file definition""" - driver_config_path = "driver_config" + driver_config_path = "driver_config.yml" board_path = "" @@ -46,24 +49,16 @@ if __name__ == "__main__": acc = 0.0 y_true = None y_pred = None - save_conf_matrix = False - conf_matrix_file = "confusion_matrix.png" - conf_matrix_classes = [str(i) for i in range(10)] + run_time = None - """DataSet Definition""" - dataset_path = f"{os.environ['HOME']}/datasets" - - # data set definition, change to your dataset - sensor_size = tonic.datasets.NMNIST.sensor_size - frame_transform = tonic.transforms.ToFrame(sensor_size=sensor_size, n_time_bins=10) - - test_set = tonic.datasets.NMNIST(save_to=dataset_path, train=False, transform=frame_transform) + save_conf_matrix = False + conf_matrix_file = "confusion_matrix.svg" + num_class = 10 + conf_matrix_classes = [str(i) for i in range(num_class)] - # batch loader - batch_size = 64 + save_array = False + - testLoader = DataLoader(test_set, batch_size=batch_size, shuffle=True, drop_last = True, collate_fn = tonic.collation.PadTensors(batch_first=True)) - if kind == "eval": acc, y_pred, y_true = evaluation( model=model, @@ -74,6 +69,7 @@ if __name__ == "__main__": quant=False ) elif kind == "qeval": + model.quantize(force_init=True, clamp=True) acc, y_pred, y_true = evaluation( model=model, testLoader=testLoader, @@ -83,7 +79,7 @@ if __name__ == "__main__": quant=True ) elif kind == "feval": - acc, y_pred, y_true = fpga_evaluation( + acc, y_pred, y_true, run_time = fpga_evaluation( model=model, driver_config=driver_config_path, board_path=board_path, @@ -96,9 +92,15 @@ if __name__ == "__main__": exit(-1) if save_conf_matrix: - confusion_matrix( + conf_matrix( y_true=y_true, y_pred=y_pred, file_name=conf_matrix_file, classes=conf_matrix_classes - ) \ No newline at end of file + ) + + if save_array: + np.save(f"{output_path}/y_true.npy", y_true) + np.save(f"{output_path}/y_pred.npy", y_pred) + if kind=="feval": + np.save(f"{output_path}/run_time.npy", run_time) \ No newline at end of file diff --git a/modneflib/modnef/templates/model.py b/modneflib/modnef/templates/model.py new file mode 100644 index 0000000000000000000000000000000000000000..6868daf3e509f7cf5215a18f678c2b364a809f62 --- /dev/null +++ b/modneflib/modnef/templates/model.py @@ -0,0 +1,184 @@ +import modnef.modnef_torch as mt +from modnef.arch_builder import * +from snntorch.surrogate import fast_sigmoid +from modnef.quantizer import * +import torch + +class MyModel(mt.ModNEFModel): + + def __init__(self, name, spike_grad=fast_sigmoid(slope=25)): + + super().__init__() + + self.name = name + + self.layer1 = mt.SLIF(in_features=2312, + out_features=128, + threshold=0.8, + v_leak=0.015, + v_min=0.0, + v_rest=0.0, + spike_grad=spike_grad, + quantizer=MinMaxQuantizer( + bitwidth=8, + signed=None + )) + + self.layer2 = mt.ShiftLIF(in_features=128, + out_features=64, + threshold=0.8, + beta=0.875, + reset_mechanism="subtract", + spike_grad=spike_grad, + quantizer=DynamicScaleFactorQuantizer( + bitwidth=8, + signed=None + )) + + self.layer3 = mt.BLIF(in_features=64, + out_features=10, + threshold=0.8, + beta=0.9, + reset_mechanism="subtract", + spike_grad=spike_grad, + quantizer=FixedPointQuantizer( + bitwidth=8, + fixed_point=7, + signed=None + )) + + def software_forward(self, input_spikes): + """ + Run layers upate + + Parameters + ---------- + input_spikes : Tensor + input spikes + + Returns + ------- + tuple of tensor + output_spike, output_mem + """ + + spk1, mem1 = self.layer1.reset_mem() + spk2, mem2 = self.layer2.reset_mem() + spk3, mem3 = self.layer3.reset_mem() + + spk_rec = [] + mem_rec = [] + + batch_size = input_spikes.shape[0] + n_steps = input_spikes.shape[1] + + for step in range(n_steps): + x = input_spikes[:, step].reshape(batch_size, -1) + + spk1, mem1 = self.layer1(x, mem1, spk1) + spk2, mem2 = self.layer2(spk1, mem2, spk2) + spk3, mem3 = self.layer3(spk2, mem3, spk3) + + spk_rec.append(spk3) + mem_rec.append(mem3) + + return torch.stack(spk_rec, dim=0), torch.stack(mem_rec, dim=0) + + def fpga_forward(self, input_spikes): + """ + Transmit input spike to FPGA + + Parameters + ---------- + input_spikes : Tensor + input spikes + + Returns + ------- + tuple of tensor + output_spike, None + """ + + def to_aer(input): + input = input.reshape(-1).to(torch.int32) + + aer = [] + for i in range(input.shape[0]): + for _ in range(input[i]): + aer.append(i) + + return aer + + if self.driver == None: + raise Exception("please open fpga driver before") + + batch_result = [] + + run_time = [] + + n_layer = 0 + + for m in self.modules(): + if isinstance(m, mt.ModNEFNeuron): + n_layer += 1 + + for sample in input_spikes: + sample_res = self.driver.run_sample(sample, to_aer, True, n_layer) + run_time.append(self.driver.sample_time) + batch_result.append([sample_res]) + + return torch.tensor(batch_result).permute(1, 0, 2), None, run_time + + def to_vhdl(self, file_name=None, output_path = ".", driver_config_path = "./driver.yml"): + """ + Generate VHDL file of model + + Parameters + ---------- + file_name = None : str + VHDL file name + if default, file name is model name + output_path = "." : str + output file path + driver_config_path = "./driver.yml" : str + driver configuration file + """ + + if file_name==None: + file_name = f"{output_path}/{self.name}.vhd" + + builder = ModNEFBuilder(self.name, 2312, 10) + + + uart = Uart_XStep_Timer( + name="uart", + input_layer_size=2312, + output_layer_size=10, + clk_freq=125_000_000, + baud_rate=921_600, + queue_read_depth=10240, + queue_write_depth=1024, + tx_name="uart_txd", + rx_name="uart_rxd" + ) + + builder.add_module(uart) + builder.set_io(uart) + + layer1_module = self.layer1.get_builder_module(f"{self.name}_layer1", output_path) + builder.add_module(layer1_module) + + layer2_module = self.layer2.get_builder_module(f"{self.name}_layer2", output_path) + builder.add_module(layer2_module) + + layer3_module = self.layer3.get_builder_module(f"{self.name}_layer3", output_path) + builder.add_module(layer3_module) + + builder.add_link(uart, layer1_module) + builder.add_link(layer1_module, layer2_module) + builder.add_link(layer2_module, layer3_module) + builder.add_link(layer3_module, uart) + + + builder.get_driver_yaml(f"{output_path}/{driver_config_path}") + builder.to_vhdl(file_name, "clock") \ No newline at end of file diff --git a/modneflib/modnef/templates/run_lib.py b/modneflib/modnef/templates/run_lib.py index 07ccb6d0bdb2cc18feca1332ccf238051cf1a1b9..7cee0717607bc16f362d25196ceb235622db2662 100644 --- a/modneflib/modnef/templates/run_lib.py +++ b/modneflib/modnef/templates/run_lib.py @@ -1,4 +1,3 @@ -import torch.nn as nn from snntorch import surrogate import torch spike_grad = surrogate.fast_sigmoid(slope=25) @@ -9,8 +8,7 @@ import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix import seaborn as sns - -def train_1_epoch(model, trainLoader, optimizer, loss, device, verbose): +def train_1_epoch(model, trainLoader, optimizer, loss, qat, device, verbose): epoch_loss = [] if verbose: @@ -20,13 +18,17 @@ def train_1_epoch(model, trainLoader, optimizer, loss, device, verbose): loader = trainLoader for _, (data, target) in enumerate(loader): - model.train() + model.train(quant=qat) + + """Prepare data""" data = data.to(device) data = data.squeeze(0) target = target.to(device) + """Forward Pass""" spk_rec, mem_rec = model(data) + """Prepare backward""" loss_val = torch.zeros((1), dtype=torch.float, device=device) for step in range(data.shape[1]): @@ -34,6 +36,7 @@ def train_1_epoch(model, trainLoader, optimizer, loss, device, verbose): epoch_loss.append(loss_val.item()) + """Backward""" model.zero_grad() loss_val.backward() optimizer.step() @@ -44,33 +47,78 @@ def train_1_epoch(model, trainLoader, optimizer, loss, device, verbose): return np.mean(epoch_loss) -def train(model, trainLoader, testLoader, optimizer, loss, device=torch.device("cpu"), validationLoader=None, n_epoch=10, best_model_name="best_model", verbose=True, save_plot=False, save_history=False, output_path="."): +def train(model, + trainLoader, + testLoader, + optimizer, + loss, + lr_scheduler = None, + qat = False, + qat_scheduler = None, + device=torch.device("cpu"), + validationLoader=None, + n_epoch=10, + best_model_name="best_model", + verbose=True, + save_plot=False, + save_history=False, + output_path="." + ): + avg_loss_history = [] acc_test_history = [] acc_val_history = [] - + lr_val_history = [] + bitwidth_val_history = [] best_acc = 0 model = model.to(device) + if qat: # we prepare model for QAT + model.init_quantizer() + model.quantize_hp() + for epoch in range(n_epoch): if verbose: print(f"---------- Epoch : {epoch} ----------") - epoch_loss = train_1_epoch(model=model, trainLoader=trainLoader, optimizer=optimizer, loss=loss, device=device, verbose=verbose) + """Model training""" + epoch_loss = train_1_epoch(model=model, trainLoader=trainLoader, optimizer=optimizer, loss=loss, device=device, verbose=verbose, qat=qat) avg_loss_history.append(epoch_loss) + """Model Validation""" if validationLoader!=None: - acc_val, _, _ = evaluation(model=model, testLoader=validationLoader, name="Validation", verbose=verbose, device=device) + acc_val, _, _ = evaluation(model=model, testLoader=validationLoader, name="Validation", verbose=verbose, device=device, quant=qat) acc_val_history.append(acc_val) - acc_test, _, _ = evaluation(model=model, testLoader=testLoader, name="Test", verbose=verbose, device=device) + """Model evaluation in test""" + acc_test, _, _ = evaluation(model=model, testLoader=testLoader, name="Test", verbose=verbose, device=device, quant=qat) acc_test_history.append(acc_test) - if best_model_name!="" and acc_test>best_acc: - torch.save(model.state_dict(), best_model_name) - best_acc = acc_test + """Save best model""" + if best_model_name!="" and acc_test>best_acc: + if not qat: + torch.save(model.state_dict(), f"{output_path}/{best_model_name}") + best_acc = acc_test + else: #if QAT + if qat_scheduler==None: # no QAT scheduler so we save our model + torch.save(model.state_dict(), f"{output_path}/{best_model_name}") + best_acc = acc_test + elif qat_scheduler.save_model(): # if QAT scheduler, we need to check if we quantize at the target bitwidth + torch.save(model.state_dict(), f"{output_path}/{best_model_name}") + best_acc = acc_test + + """Update schedulers""" + if lr_scheduler!=None: + lr_val_history.append(lr_scheduler.get_last_lr()[0]) + lr_scheduler.step() + + if qat: + model.clamp() + if qat_scheduler!=None: + bitwidth_val_history.append(qat_scheduler.bitwidth) + qat_scheduler.step() if save_history: np.save(f"{output_path}/loss.npy", np.array(avg_loss_history)) @@ -79,13 +127,19 @@ def train(model, trainLoader, testLoader, optimizer, loss, device=torch.device(" if len(acc_val_history)!=0: np.save(f"{output_path}/acc_validation.npy", np.array(acc_val_history)) + if lr_scheduler!=None: + np.save(f"{output_path}/lr_scheduler.npy", np.array(lr_scheduler)) + + if qat and qat_scheduler!=None: + np.save(f"{output_path}/qat_scheudler_bitwidth.npy", np.array(bitwidth_val_history)) + if save_plot: plt.figure() # Create a new figure plt.plot([i for i in range(n_epoch)], avg_loss_history) plt.title('Average Loss') plt.xlabel("Epoch") plt.ylabel("Loss") - plt.savefig(f"{output_path}/loss.png") + plt.savefig(f"{output_path}/loss.svg") plt.figure() if len(acc_val_history)!=0: @@ -96,7 +150,25 @@ def train(model, trainLoader, testLoader, optimizer, loss, device=torch.device(" plt.title("Accuracy") plt.xlabel("Epoch") plt.ylabel("Accuracy") - plt.savefig(f"{output_path}/accuracy.png") + plt.savefig(f"{output_path}/accuracy.svg") + + if lr_scheduler!=None: + plt.figure() + plt.plot([i for i in range(n_epoch)], lr_val_history, label="LR Value") + plt.legend() + plt.title("LR Values") + plt.xlabel("Epoch") + plt.ylabel("learning rate") + plt.savefig(f"{output_path}/lr_values.svg") + + if qat and qat_scheduler!=None: + plt.figure() + plt.plot([i for i in range(n_epoch)], lr_val_history, label="bitwidth") + plt.legend() + plt.title("Quantizer bitwidth") + plt.xlabel("Epoch") + plt.ylabel("bitwidth") + plt.savefig(f"{output_path}/quant_bitwidth.svg") return avg_loss_history, acc_val_history, acc_test_history, best_acc @@ -154,8 +226,8 @@ def __run_accuracy(model, testLoader, name, verbose, device): del spk_rec del mem_rec - y_true = torch.stack(y_true).reshape(-1) - y_pred = torch.stack(y_pred).reshape(-1) + y_true = torch.stack(y_true).reshape(-1).cpu().numpy() + y_pred = torch.stack(y_pred).reshape(-1).cpu().numpy() return (correct/total), y_pred, y_true @@ -186,7 +258,6 @@ def hardware_estimation(model, testLoader, name="Hardware Estimation", device=to return accuracy, y_pred, y_true - def fpga_evaluation(model, testLoader, board_path, driver_config, name="FPGA Evaluation", verbose=False): accuracy = 0 y_pred = [] @@ -198,9 +269,51 @@ def fpga_evaluation(model, testLoader, board_path, driver_config, name="FPGA Eva model.fpga_eval(board_path, driver_config) - accuracy, y_pred, y_true = __run_accuracy(model=model, testLoader=testLoader, name=name, verbose=verbose, device=device) + y_true = [] + y_pred = [] + run_time = [] + correct = 0 + total = 0 - return accuracy, y_pred, y_true + if verbose: + bar_format = "{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]" + loader = tqdm(testLoader, desc=name, bar_format=bar_format) + else: + loader = testLoader + + + for _, (data, target) in enumerate(loader): + + data = data.to(device) + target = target.to(device) + + y_true.append(target) + + spk_rec, mem_rec, batch_speed = model(data) + + run_time.extend(batch_speed) + + output = (spk_rec.sum(dim=0))/data.shape[1] + predicted = output.argmax(dim=1).to(device) + correct += predicted.eq(target.view_as(predicted)).sum().item() + y_pred.append(predicted) + total += target.size(0) + + if verbose: + loader.set_postfix_str(f"Accuracy : {np.mean(correct/total*100):0<3.2f} Run Time : {np.mean(batch_speed)*1e6:.3f} µs") + + del data + del target + del spk_rec + + y_true = torch.stack(y_true).cou().reshape(-1).numpy() + y_pred = torch.stack(y_pred).coup().reshape(-1).numpy() + run_time = np.array(run_time) + + + accuracy = (correct/total) + + return accuracy, y_pred, y_true, run_time def conf_matrix(y_true, y_pred, file_name, classes): cm = confusion_matrix(y_true, y_pred) diff --git a/modneflib/modnef/templates/train.py b/modneflib/modnef/templates/train.py index 03603ecb67073dd27e7d2c58b4651504b9655ab8..5d4866c4ec4e7faf5724bd760bdf042b3d2ebd2a 100644 --- a/modneflib/modnef/templates/train.py +++ b/modneflib/modnef/templates/train.py @@ -1,19 +1,21 @@ -import tonic -from torch.utils.data import DataLoader -from modnef.modnef_torch import ModNEFModelBuilder -import os from snntorch.surrogate import fast_sigmoid from run_lib import * import torch +from model import MyModel +from dataset import * +from modnef.quantizer import QuantizerScheduler if __name__ == "__main__": """Model definition""" - model_path = "model_template.json" - model = ModNEFModelBuilder(model_path, spike_grad=fast_sigmoid(slope=25)) + # model_path = "model_template.json" + # model = ModNEFModelBuilder(model_path, spike_grad=fast_sigmoid(slope=25)) + + model = MyModel("template_model", spike_grad=fast_sigmoid(slope=25)) """Optimizer""" optimizer = torch.optim.Adam(model.parameters(), lr=1e-4, betas=(0.9, 0.999)) + lr_scheduler = None #torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=5, eta_min=5e-4) """Loss""" loss = torch.nn.CrossEntropyLoss() @@ -22,29 +24,17 @@ if __name__ == "__main__": device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") """Train variable definition""" - n_epoch = 1 + n_epoch = 2 + qat = False best_model_name = "best_model" verbose = True save_plot = False save_history = False output_path = "." - - """DataSet Definition""" - dataset_path = f"{os.environ['HOME']}/datasets" - - # data set definition, change to your dataset - sensor_size = tonic.datasets.NMNIST.sensor_size - frame_transform = tonic.transforms.ToFrame(sensor_size=sensor_size, n_time_bins=10) - - train_set = tonic.datasets.NMNIST(save_to=dataset_path, train=True, transform=frame_transform) - test_set = tonic.datasets.NMNIST(save_to=dataset_path, train=False, transform=frame_transform) - # batch loader - batch_size = 64 - - trainLoader = DataLoader(train_set, batch_size=batch_size, shuffle=True, drop_last = True, collate_fn = tonic.collation.PadTensors(batch_first=True)) - testLoader = DataLoader(test_set, batch_size=batch_size, shuffle=True, drop_last = True, collate_fn = tonic.collation.PadTensors(batch_first=True)) - validationLoader = None + """Quantization Aware Training""" + qat=False + qat_scheduler = None #QuantizerScheduler(model, (8,3), 3, lambda x : FixedPointQuantizer(x, x-1, True, True)) train( model=model, @@ -53,6 +43,9 @@ if __name__ == "__main__": validationLoader=validationLoader, optimizer=optimizer, loss=loss, + lr_scheduler = lr_scheduler, + qat = qat, + qat_scheduler = qat_scheduler, device=device, n_epoch=n_epoch, best_model_name=best_model_name, diff --git a/modneflib/modnef/templates/vhdl_generation.py b/modneflib/modnef/templates/vhdl_generation.py index 82dbb71fda818e276264ed04b3e2068791d4c0ca..3ba0a9bc435ea769ed5b8f4fb48441eea649abf0 100644 --- a/modneflib/modnef/templates/vhdl_generation.py +++ b/modneflib/modnef/templates/vhdl_generation.py @@ -5,23 +5,26 @@ import os from snntorch.surrogate import fast_sigmoid from run_lib import * import torch +from model import MyModel +from dataset import * if __name__ == "__main__": """Experience name""" exp_name = "Evaluation" + """Device definition""" + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + """Model definition""" - model_path = "model_template.json" best_model_name = "best_model" - model = ModNEFModelBuilder(model_path, spike_grad=fast_sigmoid(slope=25)) + # model_path = "model_template.json" + # model = ModNEFModelBuilder(model_path, spike_grad=fast_sigmoid(slope=25)) - model.load_state_dict(torch.load(best_model_name)) + model = MyModel("template_model", spike_grad=fast_sigmoid(slope=25)) - - """Device definition""" - device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + model.load_state_dict(torch.load(best_model_name, map_location=device)) """Hardware Estimation variable definition""" verbose = True @@ -30,21 +33,10 @@ if __name__ == "__main__": """VHDL file definition""" output_path = "." file_name = "template_vhdl_model.vhd" - driver_config_path = "driver_config" - - """DataSet Definition""" - dataset_path = f"{os.environ['HOME']}/datasets" - - # data set definition, change to your dataset - sensor_size = tonic.datasets.NMNIST.sensor_size - frame_transform = tonic.transforms.ToFrame(sensor_size=sensor_size, n_time_bins=10) + driver_config_path = "driver_config.yml" - test_set = tonic.datasets.NMNIST(save_to=dataset_path, train=False, transform=frame_transform) - - # batch loader - batch_size = 64 - - testLoader = DataLoader(test_set, batch_size=batch_size, shuffle=True, drop_last = True, collate_fn = tonic.collation.PadTensors(batch_first=True)) + """Prepare model for hardware estimation""" + model.quantize(force_init=True, clamp=True) acc, y_pred, y_true = hardware_estimation( model=model,