digiproc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +48 -0
- data/LICENSE.txt +21 -0
- data/README.md +78 -0
- data/Rakefile +37 -0
- data/TODO.md +50 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/environment.rb +118 -0
- data/console_tests.rb +44 -0
- data/digiproc.gemspec +49 -0
- data/examples/analog_signals/analog_to_digital.rb +31 -0
- data/examples/analog_signals/companded-signals.png +0 -0
- data/examples/analog_signals/companding.rb +68 -0
- data/examples/analog_signals/fft-plot.png +0 -0
- data/examples/analog_signals/plot_Digiproc::FFT.png +0 -0
- data/examples/analog_signals/plot_Dsp::FFT.png +0 -0
- data/examples/analog_signals/quantization-outputs.png +0 -0
- data/examples/analog_signals/quantize_compand.rb +69 -0
- data/examples/binomial_distribution/bit_error.rb +14 -0
- data/examples/binomial_distribution/dice.rb +35 -0
- data/examples/digital_signals/_coded_frequency_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/_coded_frequency_signal,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/coded_power_spectral_density,__ts_=_1_s.png +0 -0
- data/examples/digital_signals/coded_power_spectral_density,__ts_=_2_s.png +0 -0
- data/examples/digital_signals/coded_time_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/coded_time_signal,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/frequency_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/frequency_signal,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/modulate_square_pulses.rb +9 -0
- data/examples/digital_signals/modulated_sq._pulses.png +0 -0
- data/examples/digital_signals/modulated_sq._pulses_alt.png +0 -0
- data/examples/digital_signals/power_spectral_density,__ts_=_1_s.png +0 -0
- data/examples/digital_signals/power_spectral_density,__ts_=_2_s.png +0 -0
- data/examples/digital_signals/square_signals.rb +90 -0
- data/examples/digital_signals/time_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/time_signal,_ts_=_2_s.png +0 -0
- data/examples/encoding/gray_code.rb +22 -0
- data/examples/encoding/psk.rb +91 -0
- data/examples/encoding/system_2_phase.png +0 -0
- data/examples/encoding/system_2_xmit_signal.png +0 -0
- data/examples/encoding/system_3_phase.png +0 -0
- data/examples/encoding/system_3_xmit_signal.png +0 -0
- data/examples/encoding/system_4_xmit_signal.png +0 -0
- data/examples/encoding/xor-dpsk-phase-signal-(sys1).png +0 -0
- data/examples/encoding/xor-dpsk-xmit-signal-(sys-1).png +0 -0
- data/examples/factories/Quickplot Graph.png +0 -0
- data/examples/factories/bandpass.rb +6 -0
- data/examples/fft/plot_Dsp::FFT.png +0 -0
- data/examples/fft/recieved_data_(time_domain).png +0 -0
- data/examples/fft/simple_fft_example.rb +47 -0
- data/examples/fft/unprocessed_fft.png +0 -0
- data/examples/filters/bandpass_filter.png +0 -0
- data/examples/filters/filter_a_signal.rb +38 -0
- data/examples/filters/white_noise_db_out_of_bp_filter.png +0 -0
- data/examples/filters/white_noise_mag_out_of_bp_filter.png +0 -0
- data/examples/filters/white_noise_spectra.png +0 -0
- data/examples/functions/compute_probability.rb +29 -0
- data/examples/functions/gram_schmidt.rb +10 -0
- data/examples/functions/minimize_energy.rb +29 -0
- data/examples/functions/orthoganalize.rb +18 -0
- data/examples/functions/simple_functions.rb +81 -0
- data/examples/linear_algebra/diverging_sys.rb +13 -0
- data/examples/linear_algebra/iterative_sys_of_eqns_methods.rb +27 -0
- data/examples/modulation_schemes/dpsk_2.png +0 -0
- data/examples/modulation_schemes/dpsk_256.png +0 -0
- data/examples/modulation_schemes/dpsk_freq_domain.rb +119 -0
- data/examples/modulation_schemes/psk.rb +36 -0
- data/examples/modulation_schemes/psk_2.png +0 -0
- data/examples/modulation_schemes/psk_256.png +0 -0
- data/examples/modulation_schemes/psksystem_1_xmit_signal.png +0 -0
- data/examples/modulation_schemes/psksystem_2_xmit_signal.png +0 -0
- data/examples/modulation_schemes/psksystem_3_xmit_signal.png +0 -0
- data/examples/modulation_schemes/system_1_xmit_signal.png +0 -0
- data/examples/modulation_schemes/system_2_xmit_signal.png +0 -0
- data/examples/modulation_schemes/system_3_xmit_signal.png +0 -0
- data/examples/quickplot/PlottableClass_plot.png +0 -0
- data/examples/quickplot/decorators.rb +13 -0
- data/examples/quickplot/direct_gruff.png +0 -0
- data/examples/quickplot/plot_PlottableClass.png +0 -0
- data/examples/quickplot/quickplot_vs_others.rb +85 -0
- data/examples/quickplot/random_data_quickplot,_dark.png +0 -0
- data/examples/quickplot/random_data_quickplot.png +0 -0
- data/examples/realized_gaussian/norm_dist_plot.png +0 -0
- data/examples/realized_gaussian/norm_dist_spectrum.png +0 -0
- data/examples/realized_gaussian/realized_gaussian_example.rb +23 -0
- data/lib/concerns/convolvable.rb +144 -0
- data/lib/concerns/data_properties.rb +223 -0
- data/lib/concerns/fourier_transformable.rb +178 -0
- data/lib/concerns/initializable.rb +43 -0
- data/lib/concerns/multipliable.rb +22 -0
- data/lib/concerns/os.rb +36 -0
- data/lib/concerns/plottable.rb +248 -0
- data/lib/concerns/requires_data.rb +8 -0
- data/lib/digiproc/version.rb +8 -0
- data/lib/digiproc.rb +2 -0
- data/lib/extensions/array_extension.rb +23 -0
- data/lib/extensions/core_extensions.rb +117 -0
- data/lib/factories/factories.rb +3 -0
- data/lib/factories/filter_factory.rb +83 -0
- data/lib/factories/window_factory.rb +22 -0
- data/lib/fft.rb +255 -0
- data/lib/filters/bandpass_filter.rb +43 -0
- data/lib/filters/bandstop_filter.rb +44 -0
- data/lib/filters/digital_filter.rb +59 -0
- data/lib/filters/highpass_filter.rb +27 -0
- data/lib/filters/lowpass_filter.rb +27 -0
- data/lib/functions.rb +221 -0
- data/lib/probability/binomial_distribution.rb +84 -0
- data/lib/probability/bit_generator.rb +94 -0
- data/lib/probability/gaussian_distribution.rb +29 -0
- data/lib/probability/probability.rb +234 -0
- data/lib/probability/theoretical_gaussian_distribution.rb +59 -0
- data/lib/quick_plot.rb +96 -0
- data/lib/rbplot.rb +219 -0
- data/lib/signals/analog_signal.rb +143 -0
- data/lib/signals/digital_signal.rb +181 -0
- data/lib/strategies/code/differential_encoding_strategy.rb +69 -0
- data/lib/strategies/code/gray_code.rb +75 -0
- data/lib/strategies/code/xor_differential_encoding_strategy.rb +100 -0
- data/lib/strategies/code/xor_differential_encoding_zero_angle_strategy.rb +103 -0
- data/lib/strategies/companding/custom_companding_strategy.rb +29 -0
- data/lib/strategies/convolution/bf_conv.rb +57 -0
- data/lib/strategies/fft/brute_force_dft_strategy.rb +31 -0
- data/lib/strategies/fft/inverse_fft_conjugate_strategy.rb +44 -0
- data/lib/strategies/fft/radix2_strategy.rb +84 -0
- data/lib/strategies/gaussian/gaussian_generator.rb +49 -0
- data/lib/strategies/linear_algebra/gauss_seidel_strategy.rb +90 -0
- data/lib/strategies/linear_algebra/jacobi_strategy.rb +81 -0
- data/lib/strategies/linear_algebra/sor2_strategy.rb +98 -0
- data/lib/strategies/linear_algebra/sor_strategy.rb +108 -0
- data/lib/strategies/modulation/phase_shift_keying_strategy.rb +96 -0
- data/lib/strategies/orthogonalize/gram_schmidt.rb +50 -0
- data/lib/strategies/strategies.rb +3 -0
- data/lib/strategies/window/blackman_window.rb +32 -0
- data/lib/strategies/window/hamming_window.rb +31 -0
- data/lib/strategies/window/hanning_window.rb +31 -0
- data/lib/strategies/window/kaiser_window.rb +27 -0
- data/lib/strategies/window/rectangular_window.rb +22 -0
- data/lib/strategies/window/window.rb +42 -0
- data/lib/systems/custom_system.rb +13 -0
- data/lib/systems/hilbert_transform.rb +6 -0
- data/lib/systems/matched_filter.rb +21 -0
- data/lib/systems/raised_cosine_filter.rb +11 -0
- data/lib/systems/system.rb +19 -0
- data/lib/systems/systems.rb +3 -0
- data/playground.rb +323 -0
- data/plots/_coded_frequency_signal,_ts_=_1_s.png +0 -0
- data/plots/_coded_frequency_signal,_ts_=_2_s.png +0 -0
- data/plots/coded_freq_sig_from_eqn,_ts_=_1_s.png +0 -0
- data/plots/coded_freq_sig_from_eqn,_ts_=_2_s.png +0 -0
- data/plots/coded_power_spectral_density,__ts_=_1_s.png +0 -0
- data/plots/coded_power_spectral_density,__ts_=_2_s.png +0 -0
- data/plots/coded_time_signal,_ts_=_1_s.png +0 -0
- data/plots/coded_time_signal,_ts_=_2_s.png +0 -0
- data/plots/dpsk_2.png +0 -0
- data/plots/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
- data/plots/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
- data/plots/frequency_signal,_ts_=_1_s.png +0 -0
- data/plots/frequency_signal,_ts_=_2_s.png +0 -0
- data/plots/power_spectral_density,__ts_=_1_s.png +0 -0
- data/plots/power_spectral_density,__ts_=_2_s.png +0 -0
- data/plots/psk_2.png +0 -0
- data/plots/time_signal,_ts_=_1_s.png +0 -0
- data/plots/time_signal,_ts_=_2_s.png +0 -0
- data/test-title-dark.png +0 -0
- data/test-title.png +0 -0
- metadata +322 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Allows you to solve problems whose probabilities are dictated by a binomial distribution
|
|
3
|
+
# Does not simulate a problem, but calculates probabilities based off of the binomial distribution equation
|
|
4
|
+
# See examples/binomial_distribution/dice.rb
|
|
5
|
+
|
|
6
|
+
class Digiproc::Probability::TheoreticalBinomialDistribution
|
|
7
|
+
|
|
8
|
+
attr_accessor :n, :k, :p
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# == Initialize with n, k, and p
|
|
12
|
+
# n:: [Integer] Number of trials
|
|
13
|
+
# k:: [Integer] (optional at initialization) Number of successes
|
|
14
|
+
# p:: [Numeric] Probability of success
|
|
15
|
+
def initialize(n: ,k: nil , p: )
|
|
16
|
+
@n = n.to_f
|
|
17
|
+
@k = k.to_f
|
|
18
|
+
@p = p.to_f
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Returns the Expected Value of a binomial distributed random variable (n * p)
|
|
23
|
+
## bd = Digiproc::Probability::TheoreticalBinomialDistribution.new(n:10, p: 0.5)
|
|
24
|
+
## bd.expect # => 5
|
|
25
|
+
# ie in above example, expect 5 successes (heads) in 10 flips of a coin
|
|
26
|
+
def expect
|
|
27
|
+
self.n * self.p
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# Return the Variance of the binomial distributed random variable (n * p * (1 - p))
|
|
32
|
+
# Using above example:
|
|
33
|
+
## bd.var # => 2.5
|
|
34
|
+
def var
|
|
35
|
+
self.n * self.p * ( 1 - self.p )
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# Will run `probability` method using `self.k`
|
|
40
|
+
## bd.k = 5
|
|
41
|
+
## bd.prob # => 0.24609375
|
|
42
|
+
def prob
|
|
43
|
+
self.probability(self.k)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Will run `coefficient` method using `self.k`
|
|
48
|
+
## bd.coeff # => 252.0
|
|
49
|
+
def coeff
|
|
50
|
+
coefficient(self.k)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# Calculates the probability of `k` number of `wins`
|
|
55
|
+
# `k` can be an array or a range of numbers, a variable number of args, or a single integer (see example referenced above)
|
|
56
|
+
# If k is more than one value, the probabilities will be summed together
|
|
57
|
+
# For example:
|
|
58
|
+
## bd.probability(5) # => 0.24609375
|
|
59
|
+
## bd.probability(0..5) # => 0.623046875
|
|
60
|
+
## bd.probability(1,2,5,6) # => 0.5048828125
|
|
61
|
+
## bd.probability([0,1,2,3,4,5]) # => 0.623046875
|
|
62
|
+
def probability(*k)
|
|
63
|
+
sum = 0
|
|
64
|
+
karr = k.map{ |val| val.respond_to?(:to_a) ? val.to_a : val}
|
|
65
|
+
karr.flatten!
|
|
66
|
+
karr.each do |k_val|
|
|
67
|
+
sum += self.coefficient(k_val) * ((self.p) ** (k_val)) * (1 - self.p) ** (n - k_val)
|
|
68
|
+
end
|
|
69
|
+
sum
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Returns the binomial equation coefficient for a given number of wins, ie returns n! / (k! * (n - k)!)
|
|
74
|
+
# Can take an argument of `k`, which defaults to `self.k`
|
|
75
|
+
## bd.coefficient(4) # => 210.0
|
|
76
|
+
def coefficient(k = self.k)
|
|
77
|
+
n_fact = Digiproc::Functions.fact(self.n)
|
|
78
|
+
k_fact = Digiproc::Functions.fact(k)
|
|
79
|
+
n_fact / (k_fact.to_f * (Digiproc::Functions.fact(self.n - k)))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Create random bit for testing purposes
|
|
3
|
+
#
|
|
4
|
+
class Digiproc::Probability::RandomBitGenerator
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# Returns a string representing a bitstream
|
|
8
|
+
# == Arguments
|
|
9
|
+
# size:: [Integer] number of desired bits
|
|
10
|
+
# p_0:: [Float] (between 0 and 1) probability of a bit 0 occurring (default value: 0.5)
|
|
11
|
+
# p_1:: [Float] (between 0 and 1) probability of a bit 1 occurring (default value: 0.5)
|
|
12
|
+
# random_generator:: Any random generator that follows the prototype of ruby's `Random` class. Defaults to `Random.new`
|
|
13
|
+
#
|
|
14
|
+
## bitgen = Digiproc::Probability::RandomBitGenerator
|
|
15
|
+
## bitgen.bitstream(size: 10) # => "1000010010"
|
|
16
|
+
def self.bitstream(size: , p_0: 0.5, p_1: 0.5, random_generator: Random.new)
|
|
17
|
+
stream = ''
|
|
18
|
+
size.times do
|
|
19
|
+
stream += get_bit(p_0: p_0, p_1: p_1, random_generator: random_generator).to_s
|
|
20
|
+
end
|
|
21
|
+
stream
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Returns an Integer
|
|
26
|
+
# == Arguments
|
|
27
|
+
# p_0:: [Float] (between 0 and 1) probability of a bit 0 occurring (default value: 0.5)
|
|
28
|
+
# p_1:: [Float] (between 0 and 1) probability of a bit 1 occurring (default value: 0.5)
|
|
29
|
+
# random_generator:: Any random generator that follows the prototype of ruby's `Random` class. Defaults to `Random.new`
|
|
30
|
+
#
|
|
31
|
+
## bitgen.bitstream # => 1
|
|
32
|
+
def self.get_bit(p_0: 0.5, p_1: 0.5, random_generator: Random.new)
|
|
33
|
+
raise ArgumentError.new("Probabilities must add up to 1") if p_0 + p_1 != 1
|
|
34
|
+
rnum = 1 - random_generator.rand
|
|
35
|
+
return 0 if rnum <= p_0
|
|
36
|
+
return 1
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Constructor for bitstream
|
|
41
|
+
# == Arguments
|
|
42
|
+
# p_0:: [Float] between 0 and 1: probability of a 0 bit (defaults to 0.5)
|
|
43
|
+
# p_1:: [Float] between 0 and 1: probability of a 1 bit (defaults to 0.5)
|
|
44
|
+
## bitstream = bitgen.new_bitstream
|
|
45
|
+
def self.new_bitstream(p_0: 0.5 , p_1: 0.5)
|
|
46
|
+
self.new(bits_per_symbol: 1, p_0: p_0, p_1: p_1)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Constructor for symbol_stream
|
|
51
|
+
# == Arguments
|
|
52
|
+
# bits_per_symbol:: [Integer] set the number of bits for each symbol
|
|
53
|
+
# p_0:: [Float] between 0 and 1: probability of a 0 bit (defaults to 0.5)
|
|
54
|
+
# p_1:: [Float] between 0 and 1: probability of a 1 bit (defaults to 0.5)
|
|
55
|
+
## bitstream = bitgen.new_bitstream
|
|
56
|
+
def self.new_symbol_stream(bits_per_symbol: , p_0: 0.5, p_1: 0.5)
|
|
57
|
+
self.new(bits_per_symbol: bits_per_symbol, p_0: p_0, p_1: p_1)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
attr_accessor :bits_per_symbol, :p_0, :p_1, :random_generator
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# == Arguments
|
|
64
|
+
# # bits_per_symbol:: [Integer] set the number of bits for each symbol
|
|
65
|
+
# p_0:: [Float] between 0 and 1: probability of a 0 bit (defaults to 0.5)
|
|
66
|
+
# p_1:: [Float] between 0 and 1: probability of a 1 bit (defaults to 0.5)
|
|
67
|
+
#
|
|
68
|
+
## bitstream = bitgen.new(bits_per_symbol: 1) => same as bitgen.new_bitstream
|
|
69
|
+
def initialize(bits_per_symbol: , p_0: 0.5, p_1: 0.5 )
|
|
70
|
+
raise ArgumentError.new("Probabilities of bits must add up to 1") if p_0 + p_1 != 1
|
|
71
|
+
@bits_per_symbol, @p_0, @p_1, @random_generator = bits_per_symbol, p_0, p_1, Random.new
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# == Arguments
|
|
76
|
+
# size:: [Integer] number of symbols to be generated (or bits if self.bits_per_symbol is 1)
|
|
77
|
+
## bistream = Digiproc::Probability::RandomBitGenerator.new_bitstream
|
|
78
|
+
## symstream = Digiproc::Probability::RandomBitGenerator.new_symbol_stream(bits_per_symbol: 8)
|
|
79
|
+
## bitstream.generate(10) # => "0001110010"
|
|
80
|
+
## symstream.generate(4) # => ["10100111", "00111100", "00111010", "10000100"]
|
|
81
|
+
def generate(size)
|
|
82
|
+
if self.bits_per_symbol == 1
|
|
83
|
+
return self.class.bitstream(size: size, p_0: @p_0, p_1: @p_1, random_generator: @random_generator)
|
|
84
|
+
else
|
|
85
|
+
signal = []
|
|
86
|
+
size.times do
|
|
87
|
+
signal << self.class.bitstream(size: @bits_per_symbol, p_0: @p_0, p_1: @p_1, random_generator: @random_generator)
|
|
88
|
+
end
|
|
89
|
+
return signal
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Class to create a sample of Gaussian Distributed values
|
|
3
|
+
class Digiproc::Probability::GaussianDistribution
|
|
4
|
+
|
|
5
|
+
attr_accessor :mean, :stddev, :generator, :data
|
|
6
|
+
attr_reader :size
|
|
7
|
+
|
|
8
|
+
include Digiproc::Convolvable::InstanceMethods, Digiproc::Initializable, Digiproc::FourierTransformable
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# == Initialize arguments
|
|
12
|
+
# mean:: [Float] mean of the population
|
|
13
|
+
# stddev:: [Float] standard deviation of the population
|
|
14
|
+
# size:: [Integer] number of datapoints
|
|
15
|
+
# generator:: Strategy for making Gaussian values. Defaults to Digiproc::Strategies::GaussianGeneratorBoxMullerStrategy.new
|
|
16
|
+
def initialize(mean: , stddev: , size: ,generator: Digiproc::Strategies::GaussianGeneratorBoxMullerStrategy.new)
|
|
17
|
+
@mean, @stddev, @generator, @size = mean, stddev, generator, size
|
|
18
|
+
generator.mean = mean
|
|
19
|
+
generator.stddev = stddev
|
|
20
|
+
data = []
|
|
21
|
+
size.times do
|
|
22
|
+
data << generator.rand
|
|
23
|
+
end
|
|
24
|
+
@data = data
|
|
25
|
+
initialize_modules(Digiproc::FourierTransformable => {time_data: data})
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
end
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Module supplying probability functions
|
|
3
|
+
module Digiproc::Probability
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# Interchangable strategy
|
|
7
|
+
#Strategy requirements:
|
|
8
|
+
#can be initialized with no arguments (gives mean = 0 and stddev = 1), or with a mean and stddev
|
|
9
|
+
#instance method: #rand returns normal distribution with mean = 0, stddev = 1
|
|
10
|
+
@gaussian_generator = Digiproc::Strategies::GaussianGeneratorBoxMullerStrategy.new
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
# Returns a new instance of a normal number random generator, which contains an instance method #rand which will return a
|
|
15
|
+
# single random number from the normal distribution
|
|
16
|
+
# == Arguments
|
|
17
|
+
# mean:: [Float] mean of the distribution defaults to 0
|
|
18
|
+
# stddev:: [Float] standard deviation of the distribution defaults to 1
|
|
19
|
+
## gen = Digiproc::Probability.normal_random_generator(100, 15) # => return rand gen of IQ scores
|
|
20
|
+
## scores = []
|
|
21
|
+
## 10.times do
|
|
22
|
+
## scores << gen.rand
|
|
23
|
+
## end
|
|
24
|
+
## puts scores # => [98.22222989456891, 102.78474153867536, 130.32345913801984, 90.68720464516447, 94.45065665514275, 100.46571157090098, 105.40955807686252, 85.9616084359681, 103.05219341559669, 96.62475435904693]
|
|
25
|
+
def self.normal_random_generator(mean = 0, stddev = 1)
|
|
26
|
+
@gaussian_generator.class.new(mean, stddev)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# == Arguments
|
|
31
|
+
# size:: [Integer] number of returns expected, defaults to 1
|
|
32
|
+
# If `size` is 1, returns a single number from the distribution
|
|
33
|
+
# For `size` > 1, return an array of numbers from the distribution
|
|
34
|
+
## Digiproc::Probability.nrand(5) # => [-0.4870987490684469, 0.48974360810927925, -0.3722088483576355, -0.2829898781247938, 0.12540113064787742]
|
|
35
|
+
## Digiproc::Probability.nrand # => 0.7978655621417761
|
|
36
|
+
def self.nrand(size = 1)
|
|
37
|
+
return @gaussian_generator.rand if size == 1
|
|
38
|
+
rand_nums = []
|
|
39
|
+
size.times do
|
|
40
|
+
rand_nums << @gaussian_generator.rand
|
|
41
|
+
end
|
|
42
|
+
return rand_nums
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
# Returns [Float] the mean of the inputted data
|
|
47
|
+
# == Arguments
|
|
48
|
+
# data:: [Array] data to be evaluated
|
|
49
|
+
## dat = [4,12,19,4,8,9]
|
|
50
|
+
## Digiproc::Probability.mean(dat) # => 9.333333333333334
|
|
51
|
+
def self.mean(data)
|
|
52
|
+
data.sum / data.length.to_f
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# Returns [Float] the variance of the inputted data
|
|
57
|
+
# == Arguments
|
|
58
|
+
# data:: [Array] data to be evaluated
|
|
59
|
+
# variance = sum_over_datavals( (dataval - mean) ^ 2 ) / (number_of_datavals - 1)
|
|
60
|
+
## dat = [4,12,19,4,8,9]
|
|
61
|
+
## Digiproc::Probability.variance(dat) # => 31.866666666666664
|
|
62
|
+
def self.variance(data)
|
|
63
|
+
mu = mean(data)
|
|
64
|
+
summation = data.map{ |val| (val - mu) ** 2 }.sum
|
|
65
|
+
summation.to_f / (data.length - 1)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# Returns [Float] the variance of inputted data for a stationary process
|
|
70
|
+
# (stationary proces = mean, variance, autocorrelation do not change with time)
|
|
71
|
+
# Will run faster over large datasets because each val in data is not undergoing a subtraction with mu
|
|
72
|
+
# If the process is not stationary, an incorrect result with be given
|
|
73
|
+
# == Arguments
|
|
74
|
+
# data:: [Array] data to be evaluated
|
|
75
|
+
# stat_var = sum_over_datavals( val^2 ) / number_ov_vals
|
|
76
|
+
## dat = [4,12,19,4,8,9]
|
|
77
|
+
## Digiproc::Probability.stationary_variance(dat) # => 26.555555555555543
|
|
78
|
+
def self.stationary_variance(data)
|
|
79
|
+
mu = mean(data)
|
|
80
|
+
summation = data.map{ |val| val ** 2 }.sum
|
|
81
|
+
(summation.to_f / data.length) - (mu ** 2)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
# Returns [Float] the standard deviation of the inputted data
|
|
86
|
+
# == Arguments
|
|
87
|
+
# data:: [Array] data to be evaluated
|
|
88
|
+
# stddev = sqrt(variance)
|
|
89
|
+
## dat = [4,12,19,4,8,9]
|
|
90
|
+
## Digiproc::Probability.stddev # => 5.645056834671079
|
|
91
|
+
def self.stddev(data)
|
|
92
|
+
variance(data) ** (0.5)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
##
|
|
97
|
+
# Returns [Float] the covariance (if process is stationary) of two datasets
|
|
98
|
+
# == Arguments
|
|
99
|
+
# data1:: [Array] first dataset
|
|
100
|
+
# data2:: [Array] second datatset
|
|
101
|
+
## dat = [4,12,19,4,8,9]
|
|
102
|
+
## dat2 = [19, 20, 35, 15, 13, 17]
|
|
103
|
+
## Digiproc::Probability.stationary_covariance(dat, dat2) # => 31.22222222222223
|
|
104
|
+
|
|
105
|
+
def self.stationary_covariance(data1, data2)
|
|
106
|
+
raise ArgumentError.new("Datasets must be of equal length") if data1.length != data2.length
|
|
107
|
+
xcs = data1.dot data2
|
|
108
|
+
mu1 = mean(data1)
|
|
109
|
+
mu2 = mean(data2)
|
|
110
|
+
(xcs / (data1.length.to_f)) - (mu1 * mu2)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
##
|
|
114
|
+
# Returns [Float] covariance of two datasets
|
|
115
|
+
# == Arguments
|
|
116
|
+
# data1:: [Array] first dataset
|
|
117
|
+
# data2:: [Array] second datatset
|
|
118
|
+
## dat = [4,12,19,4,8,9]
|
|
119
|
+
## dat2 = [19, 20, 35, 15, 13, 17]
|
|
120
|
+
## Digiproc::Probability.ovariance(dat, dat2) # => 37.46666666666667
|
|
121
|
+
def self.covariance(data1, data2)
|
|
122
|
+
raise ArgumentError.new("Datasets must be of equal length") if data1.length != data2.length
|
|
123
|
+
mu1 = mean(data1)
|
|
124
|
+
mu2 = mean(data2)
|
|
125
|
+
summation = 0
|
|
126
|
+
for i in 0...data1.length do
|
|
127
|
+
summation += ((data1[i] - mu1) * (data2[i] - mu2))
|
|
128
|
+
end
|
|
129
|
+
summation.to_f / (data1.length - 1)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
##
|
|
133
|
+
# Calculates Pearson's correlation coefficient of two datasets
|
|
134
|
+
# == Arguments
|
|
135
|
+
# data1:: [Array] first dataset
|
|
136
|
+
# data2:: [Array] second dataset
|
|
137
|
+
## dat = [4,12,19,4,8,9]
|
|
138
|
+
## dat2 = [19, 20, 35, 15, 13, 17]
|
|
139
|
+
## Digiproc::Probability.correlation_coefficient(dat, dat2) # => 0.84450000297225
|
|
140
|
+
## cdat = [1,2,3,4,5]
|
|
141
|
+
## ddat = cdat.map{ |v| v * -29 }
|
|
142
|
+
## Digiproc::Probability.correlation_coefficient(cdat, ddat) # => -1.0
|
|
143
|
+
def self.correlation_coefficient(data1, data2)
|
|
144
|
+
covar = covariance(data1, data2)
|
|
145
|
+
var1 = variance(data1)
|
|
146
|
+
var2 = variance(data2)
|
|
147
|
+
return covar.to_f / ((var1 ** 0.5) * (var2 ** 0.5))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
##
|
|
151
|
+
# Alias for #variance
|
|
152
|
+
def self.var(d)
|
|
153
|
+
variance(d)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
##
|
|
157
|
+
# Alias for #covariance
|
|
158
|
+
def self.cov(d1, d2)
|
|
159
|
+
covariance(d1,d2)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
##
|
|
163
|
+
# Alias for #correlation_coefficient
|
|
164
|
+
def self.corr_coeff(d1, d2)
|
|
165
|
+
correlation_coefficient(d1, d2)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
##
|
|
169
|
+
# Returns the error function output [Float] of the input ie:
|
|
170
|
+
# For an input x and a normal distribution with mean 0 and variance 0.5, it is the probability a random variable will be between -x and x
|
|
171
|
+
# Mirrors Math.erf(x)
|
|
172
|
+
# == Arguments
|
|
173
|
+
# x:: [Float]
|
|
174
|
+
## Digiproc::Prbobability.erf(0.3) # => 0.32862675945912734
|
|
175
|
+
def self.erf(x)
|
|
176
|
+
Math.erf(x)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
##
|
|
180
|
+
# Returns the erfc [Float] per Math.erfc
|
|
181
|
+
# For an input x and a normal distribution with mean 0 and variance 0.5, it is the probability a random variable will not be between -x and x
|
|
182
|
+
# == Arguments
|
|
183
|
+
# x:: [Float]
|
|
184
|
+
## Digiproc::Prbobability.erfc(0.3) # => 0.6713732405408727
|
|
185
|
+
def self.erfc(x)
|
|
186
|
+
Math.erfc(x)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
##
|
|
190
|
+
# Returns [Float] probability that a random variable from a normal distribution of a certain mean and standard deviation will be less than a value x
|
|
191
|
+
# == Arguments
|
|
192
|
+
# x:: [Float] the value which the probability will be evaluated that a random variable will be below
|
|
193
|
+
# mean:: [Float] the mean of the normal distribution (defaults to 0)
|
|
194
|
+
# stddev:: [Float] the standard deviation of the normal distribution (defaults to 1)
|
|
195
|
+
## Digiproc::Probability.normal_cdf(0.5) # => 0.691462461274013
|
|
196
|
+
## Digiproc::Probability.normal_cdf(140, 100, 15) # => 0.9961696194324102
|
|
197
|
+
def self.normal_cdf(x, mean = 0, stddev = 1)
|
|
198
|
+
1 - normal_q(x, mean, stddev)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
##
|
|
202
|
+
# Returns [Float] the probability that a random variable from a normal distribution of a certain mean and standard deviation will be greater than a value x
|
|
203
|
+
# == Arguments
|
|
204
|
+
# x:: [Float] the value which the probability will be evaluated that a random variable will be below
|
|
205
|
+
# mean:: [Float] the mean of the normal distribution (defaults to 0)
|
|
206
|
+
# stddev:: [Float] the standard deviation of the normal distribution (defaults to 1)
|
|
207
|
+
## Digiproc::Probability.normal_q(0.5) # => 0.30853753872598694
|
|
208
|
+
## Digiproc::Probability.normal_q(140, 100, 15) # => 0.0038303805675897395
|
|
209
|
+
def self.normal_q(x, mean = 0, stddev = 1)
|
|
210
|
+
xformed_x = (x - mean) / stddev.to_f
|
|
211
|
+
0.5 * erfc(xformed_x / (2 ** 0.5))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
##
|
|
215
|
+
# Returns [Hash] the discrete probability distribution function of an inputted array of values
|
|
216
|
+
# == Arguments
|
|
217
|
+
# data:: [Array] discrete values in a dataset
|
|
218
|
+
## dat = [3,3,4,5,4,5,6,5,6,5,7,6,5,4]
|
|
219
|
+
## Digiproc::Probability.pdf(dat) # => {3=>2, 4=>3, 5=>5, 6=>3, 7=>1}
|
|
220
|
+
def self.pdf(data)
|
|
221
|
+
pdf = {}
|
|
222
|
+
data.each do |datapoint|
|
|
223
|
+
pt = datapoint.round(2)
|
|
224
|
+
if pdf[pt].nil?
|
|
225
|
+
pdf[pt] = 1
|
|
226
|
+
else
|
|
227
|
+
pdf[pt] += 1
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
pdf
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Class to calculate probabilities based off of a gaussian distributed random variable.
|
|
3
|
+
class Digiproc::Probability::TheoreticalGaussianDistribution
|
|
4
|
+
|
|
5
|
+
attr_reader :mean, :stddev, :variance
|
|
6
|
+
|
|
7
|
+
def initialize(mean: , stddev: , generator_strategy: Digiproc::Strategies::GaussianGeneratorBoxMullerStrategy)
|
|
8
|
+
@mean, @stddev = mean, stddev
|
|
9
|
+
@variance = stddev ** 2
|
|
10
|
+
@generator_strategy = generator_strategy.new(mean, stddev)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
# Return the cdf [Float] of an input x [Float]
|
|
15
|
+
## gd = Digiproc::Probability::TheoreticalGaussianDistribution.new(mean: 0, stddev: 1)
|
|
16
|
+
## gd.cdf(0.3) # => 0.6179114221889526
|
|
17
|
+
def cdf(x)
|
|
18
|
+
1 - q(x)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Return the q value [Float] of an input x [Float]
|
|
23
|
+
## gd = Digiproc::Probability::TheoreticalGaussianDistribution.new(mean: 0, stddev: 1)
|
|
24
|
+
## gd.q(0.3) # => 0.3820885778110474
|
|
25
|
+
def q(x)
|
|
26
|
+
xform_x = xform_x_to_standard(x)
|
|
27
|
+
0.5 * Math.erfc(xform_x / (2 ** 0.5))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# Return the probability [Float] of the random variable being between an upper [Float] and lower [Float] value
|
|
32
|
+
## gd = Digiproc::Probability::TheoreticalGaussianDistribution.new(mean: 0, stddev: 1)
|
|
33
|
+
## gd.p_between(-1, 1) # => 0.6826894921370859
|
|
34
|
+
def p_between(lower, upper)
|
|
35
|
+
cdf(upper) - cdf(lower)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# Return the probability [Float] of the random variable being outside an upper [Float] and lower [Float] value
|
|
40
|
+
## gd = Digiproc::Probability::TheoreticalGaussianDistribution.new(mean: 0, stddev: 1)
|
|
41
|
+
## gd.p_outside(-1, 1) # => 0.31731050786291415
|
|
42
|
+
def p_outside(lower, upper)
|
|
43
|
+
cdf(lower) + q(upper)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Outputs a single random number from the gaussian distribution
|
|
48
|
+
## gd.rand # => 1.4173209026395848
|
|
49
|
+
def rand
|
|
50
|
+
@generator_strategy.rand
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def xform_x_to_standard(x)
|
|
57
|
+
(x - self.mean) / self.stddev.to_f
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/quick_plot.rb
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Class for quickly plotting data
|
|
3
|
+
class Digiproc::QuickPlot
|
|
4
|
+
|
|
5
|
+
extend Digiproc::Plottable::ClassMethods
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# Quickly plot data. Either data OR x AND y are required. If just data is inputted, x values will be the index of the value in the data array
|
|
11
|
+
# == Input Args
|
|
12
|
+
# x:: Array[Numeric] (required if data is not included)
|
|
13
|
+
# y:: Array[Numeric] (required if data is not included)
|
|
14
|
+
# data:: Array[Numeric] (required if x and y are not included)
|
|
15
|
+
# title (Optional):: String
|
|
16
|
+
# x_label (Optional):: String
|
|
17
|
+
# y_label (Optional):: String
|
|
18
|
+
# labels (Optional):: Array[String] x value labels
|
|
19
|
+
# data_name (Optional):: name of data for legend
|
|
20
|
+
# label_map (Optional):: Lambda - mapping data index to an x label for that index
|
|
21
|
+
# dark (Optional):: Boolean - whether or not to use dark mode (defaults to false)
|
|
22
|
+
# path (Optional):: Path to save plot picture to. Defauilts to "./plots"
|
|
23
|
+
def self.plot(x: nil,y: nil, data: nil, title: nil, x_label: nil, y_label: nil, labels: nil, data_name: "data", label_map: nil, dark: false, path: "./plots/")
|
|
24
|
+
xyname, dataname = nil, nil
|
|
25
|
+
if not x.nil?
|
|
26
|
+
if evenly_spaced?(x)
|
|
27
|
+
label_map = Digiproc::Functions.map_to_eqn(0, x.length, x.min, x.max)
|
|
28
|
+
data = y
|
|
29
|
+
y, x = nil, nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
data.nil? ? (xyname = data_name) : (dataname = data_name)
|
|
33
|
+
qplot(x: x, y: y, data: data, data_name: dataname, xyname: xyname, label_map: label_map, filename: to_filename(title), path: path) do |g|
|
|
34
|
+
g.title = title if not title.nil?
|
|
35
|
+
g.x_axis_label = x_label if not x_label.nil?
|
|
36
|
+
g.y_axis_label = y_label if not y_label.nil?
|
|
37
|
+
g.labels = labels if not labels.nil?
|
|
38
|
+
g.theme = Digiproc::Plottable::Styles::MIDNIGHT if dark
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.step()
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
# Plots a pi plot
|
|
47
|
+
def self.pi_plot(label ="0.5")
|
|
48
|
+
z = Digiproc::Functions.zeros(26)
|
|
49
|
+
o = Digiproc::Functions.ones(50)
|
|
50
|
+
y = z + o + z
|
|
51
|
+
x = Digiproc::Functions.linspace(-2, 2, 102)
|
|
52
|
+
plot(x: x, y: y, title: "PI fn" ,labels: {-1 => "-#{label}", 0 => "0", 1 => label})
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Plots a lambda plot
|
|
58
|
+
def self.lambda_plot(label = "1")
|
|
59
|
+
eqn = ->(t){ t.abs <= 1 ? (1 - t.abs) : 0}
|
|
60
|
+
x = Digiproc::Functions.linspace(-2, 2, 102)
|
|
61
|
+
plot(data: x.map{|t| eqn.call(t)},title: "Lambda fn" ,labels: {26 => "-#{label}", 51 => 0 ,76 => label})
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# Plots an input equation (lambda)
|
|
66
|
+
# == Input args
|
|
67
|
+
# eqn:: lambda, equation to pe plotted
|
|
68
|
+
# sample_times:: Array[Numeric] input values to lambda equation
|
|
69
|
+
# title (Optional):: String
|
|
70
|
+
# x_label (Optional):: String
|
|
71
|
+
# y_label (Optional):: String
|
|
72
|
+
def self.plot_eq(eqn: , sample_times: ,title: nil, x_label: nil, y_label: nil)
|
|
73
|
+
data = process(eqn, sample_times)
|
|
74
|
+
plot(data: data, title: title, x_label: x_label, y_label: y_label)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def process(eqn, locations)
|
|
78
|
+
locations.map{ |n| eqn.call(n) }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def self.evenly_spaced?(data)
|
|
84
|
+
interval = data[1] - data[0]
|
|
85
|
+
for i in 2...data.length do
|
|
86
|
+
new_int = data[i] - data[i-1]
|
|
87
|
+
return false if new_int.round(10) != interval.round(10)
|
|
88
|
+
end
|
|
89
|
+
true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.to_filename(title)
|
|
93
|
+
title.nil? ? "Quickplot Graph" : title.downcase.gsub(" ", "_")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|