Feeds
Mike Driscoll: Ruff – The Fastest Python Linter and Formatter Just Got Faster!
I’m a little late in reporting on this topic, but Ruff put out an update in April 2024 that includes a hand-written recursive descent parser. This update is in version 0.4.0 and newer.
Ruff’s new parser is >2x faster, translating to a 20-40% speedup for all linting and formatting invocations. Ruff’s announcement includes some statistics to show improvements that are worth checking out.
What’s This New Parser?I’ve never tried writing a code parser, so I’ll have to rely on Ruff’s announcement to explain this. Basically, when you are doing static analysis, you will turn the source code into Abstract Syntax Trees (ASTs), which you can then analyze. Python has an AST module built in for this purpose. Ruff is written in Rust, though, so their AST analyzer is also written in Rust.
The original parser was called a generated parser, specifically LALRPOP. The parser requires a grammar to be defined in a Domain Specific Language (DSL), which is then converted into executable code for the generator.
Ruff’s new hand-written parser is a recursive descent parser. Follow that link to Wikipedia to learn all the nitty gritty details.
Their team created a hand-written parser to give them more control and flexibility over the parsing process, making it easier to work on the many weird edge cases they need to support. They also created a new parser to make Ruff faster and provide better error messages and error resilience.
Wrapping UpRuff is great and makes linting and formatting your Python code so much faster. You can learn much more about Ruff in my other articles on this topic:
The post Ruff – The Fastest Python Linter and Formatter Just Got Faster! appeared first on Mouse Vs Python.
Hello Planet KDE!
Gábor Hojtsy: 15 reasons I am excited about Drupal's new Starshot initiative
Drupal project lead Dries Buytaert just unveiled the Drupal Starshot Initiative and I couldn't be more excited! Starshot is all about putting users first. Enhancing Drupal core with additional components tailored to everyday users of the interface. Technically, it builds on Drupal core's robust foundation, utilizing Automatic Updates/Package Manager for seamless installation and maintenance, Recipes for the base system and on-demand additions, and Project Browser to help with recipe and extension discovery.
As I was talking to people about Starshot at DrupalCon Portland I realized it is easy to overlook how fundamental the changes proposed are in terms of how Drupal will innovate and the benefits end users will enjoy. At the same time those that did not have time to watch the whole keynote had understandable misconceptions about its nature. Some were concerned the fundamental changes are happening in the architecture, or a fork / parallel project is being set up. That is not where/how Starshot revolutionizes Drupal though. To address those misconceptions, I already worked on answers to frequently asked questions and published on the Starshot page.
This blog post will focus on the benefits that I see. I had the chance to participate in two hours of Starshot BoFs and then an hour long Q&A session at DrupalCon Portland. I also covered more technical details of Starshot's architecture in my Drupal 11 talk (including the most popular question on what the Launch button might lead to). Finally I had countless conversations with people at the event. Maybe confirmation bias, but there were only a couple people I talked to that were entirely skeptical. On the other hand I got very different viewpoints on what will the benefits be depending on who I talked to.
I think all of those are great, so I compiled them. Let's see 15 different reasons why I am super excited about Starshot and how it is very different from previous initiatives.
Gábor Hojtsy Sat, 05/11/2024 - 13:49This week in KDE: our cup overfloweth with cool stuff for you
This week a lot of work that has been in progress for weeks got merged! So check out the free goodies! And isn’t that amazing? Free stuff day after day, week after week. No price tag, no ads, no spying, no activation, no subscription, no nonsense. Just good work donated to the public. And not only from KDE, but the software stack we rely on, the distributions that make our software available, and on and on! We really live in an amazing time, folks.
New FeaturesBy default, Dolphin now selects everything in a folder when you double-click on its view background, and also lets you configure it to do other things instead–up to and including running custom terminal commands! (George Florea Bănuș, Dolphin 24.08. Link)
Elisa now lets you shuffle the playlist contents by album, not just by track (Bart De Vries, Elisa 24.08. Link)
System Settings now features a page where you can turn on and configure remote login based on the Remote Desktop Protocol (Akseli Lahtinen and me: Nate Graham, Plasma 6.1. Link 1 and link 2)
Final UI is still a bit in flux; you’ll notice that the merge request at the second link isn’t merged yet, and there are some obvious misalignmentsOn Wayland, KWin can now be configured to pull color profile information from the monitor’s EDID metadata where present. Note that color profile information in EDID metadata is often wrong, so use this setting with caution. The feature includes inline help text to make you aware of this, too. (Xaver Hugl, Plasma 6.2. Link 1, link 2, link 3, and link 4)
UI ImprovementsIt’s now more obvious how you end a screen recording in Spectacle: the “currently recording” icon it shows in the System Tray now animates to get your attention a bit more, and Spectacle also sends a system notification to tell you about it (Noah Davis, Spectacle 24.08. Link)
When the clock disappears on Plasma’s lock screen, the cursor does too, which makes it possible to use the screen locker as a true screensaver if you give it a wallpaper plugin that has some kind of animated effect (Someone amazing, Plasma 6.0.4. Link)
It’s now obvious how you close Plasma 6’s fancy new panel configuration dialog: it has a “Done” button in the corner! (Taro Tanaka and Me: Nate Graham, Plasma 6.1. Link):
When you disconnect from a network while it’s showing the speed graph view, it now automatically switches over to the info view (Eugene Popov, Plasma 6.1. Link)
Smooth scrolling in KDE’s QML-based apps is now optional (though still on by default). It’s also possible that 3rd-party apps will eventually read and respect this setting, as I recently noticed Firefox does for our global animation speed setting (Nathan Misner, Plasma 6.2. Link 1 and link 2)
Small in-window dialogs in QtQuick-based software have gotten a visual overhaul to remove everything not visually necessary, which gives the text and buttons more focus (Carl Schwan, Frameworks 6.3. Link 1 and link 2)
Also not the final appearance, but this is the general design direction right nowCommand bars in QtWidgets-based apps have also gotten a visual overhaul to match this more minimalistic style (Eugene Popov, Frameworks 6.3. Link):
Bug FixesElisa no longer freezes when you open Party Mode while music is playing and the headerbar is collapsed or sized in certain ways (Pedro Nishiyama, Elisa 24.05. Link)
Fixed two longstanding issues that could cause Plasma to crash when it didn’t find all the screens it expected to find when waking up or booting the system (Marco Martin, Plasma 6.0.4 and 6.0.5. Link 1 and link 2)
Discover no longer misleadingly and incorrectly claims that apps with no licenses listed are proprietary (Harald Sitter, Plasma 6.0.5. Link)
Fixed a Plasma 6 regression that caused Discover to show annoying and ignorable error messages when viewing pages for content from store.kde.org (Harald Sitter, Plasma 6.0.5. Link)
The search/filter field in Plasma’s Printers widget now works (Mike Noe, Plasma 6.0.5. Link)
Fixed a Plasma 6 regression that caused panel widgets to overlap when you have an Activity Pager widget somewhere on a horizontal panel (Edo Friedman, Plasma 6.0.5. Link)
KWin is now more reliable about turning off screens in response to hardware and driver quirks that previously made this less than reliable with certain setups (Arsen Arsenović, Plasma 6.0.5. Link)
Config windows for Plasma’s System Monitor and System Tray widgets and the power profiles OSD no longer have mismatched colors for some UI controls and icons when using a mixed light/dark global theme such as Breeze Twilight (Akseli Lahtinen, Evgeniy Harchenko, and Nicolas Fella, Plasma 6.0.5. Link 1, link 2, and link 3)
Searching for something in Plasma’s Clipboard widget now returns a message with the correct text (“No matches”) when your search didn’t match anything (Thomas Duckworth, Plasma 6.0.5. Link)
Plasma’s Task Manager widget was internally refactored to simplify some old crusty code, which fixes two prominent layout glitches, including a Plasma 6 regression where tasks would overlap with adjacent widgets when in multi-row mode (Marco Martin, Plasma 6.1. Link 1 and link 2)
Other bug information of note:
- 3 Very high priority Plasma bugs (same as last week). Current list of bugs
- 38 15-minute Plasma bugs (up from 35 last week). Current list of bugs
- 133 KDE bugs of all kinds fixed over the last week Full list of bugs
The plasma-workspace git repo has adopted a merge request template to guide people towards writing good commit messages, testing their changes, and including before-and-after screenshots. If this works out well, we’ll expend it elsewhere too (me: Nate Graham, link)
We now have a bug announcement bot that yells at us about the number of high and very high priority Plasma bugs, as well as the number of current known regressions (Ben Bonacci, link):
…And Everything ElseThis blog only covers the tip of the iceberg! If you’re hungry for more, check out https://planet.kde.org, where you can find more news from other KDE contributors.
How You Can HelpThe KDE organization has become important in the world, and your time and labor have helped to bring it there! But as we grow, it’s going to be equally important that this stream of labor be made sustainable, which primarily means paying for it. Right now the vast majority of KDE runs on labor not paid for by KDE e.V. (the nonprofit foundation behind KDE, of which I am a board member), and that’s a problem. We’ve taken steps to change this with paid technical contractors—but those steps are small due to growing but still limited financial resources. If you’d like to help change that, consider donating today!
Otherwise, visit https://community.kde.org/Get_Involved to discover other ways to be part of a project that really matters. Each contributor makes a huge difference in KDE; you are not a number or a cog in a machine! You don’t have to already be a programmer, either. I wasn’t when I got started. Try it, you’ll like it! We don’t bite!
HDR and color management in KWin, part 3
Since the last two posts about this topic (part one, part two) there has been some more progress, so let’s take a look.
Brightness ControlIn Plasma 6.0, when HDR is enabled, you get to choose the brightness of SDR content in the display settings:
This is however not convenient to change quickly and doesn’t apply to HDR content, which so far is always presented at 100% brightness. In Plasma 6.1, you’ll be able to use the same ways to control brightness as on laptops or with displays that support DDC/CI in SDR mode: Just drag the brightness slider, press keyboard shortcuts or scroll on the brightness icon in the system tray, and it’ll dim the whole screen like you’d expect.
Powerdevil unfortunately only supports controlling the brightness of a single screen right now, and that limitation also applies here. The API it uses to communicate with KWin is per display though, so once powerdevil supports multiple screens, it’ll work for HDR displays too.
More accurate colors without a ColorimeterMany new displays have a color gamut much wider than sRGB, and assume that their input signal is fitting for their color gamut - which means that, unless you use an ICC profile, colors will be much more saturated than they should be. There’s a few ways to correct this:
- find an sRGB option in your monitor’s OSD. This is usually pretty accurate if it’s available, but also limits all apps to sRGB
- buy a Colorimeter and profile your display. That costs money though, and the display profiling situation on Linux isn’t in great shape at the moment (DisplayCAL on Flathub is 4 years old, my last attempt at building it on Fedora didn’t work, and it only works correctly on Xorg atm)
- find an ICC profile for your display on the Internet and use that, hoping the display doesn’t deviate too much from the one that was profiled
There is a fourth option though: Use the color information from the display’s EDID. In Plasma 6.1, you can simply select this in the display settings.
Note that it comes with some caveats too:
- the EDID only describes colors with the default display settings, so if you change the “picture mode” or similar things in the display settings, the values may not be correct anymore
- the manufacturer may not measure every panel and just put generic values for the display model into the EDID
- the manufacturer may put completely wrong values in there (which is why this is disabled by default)
- even when correct values are provided, ICC profiles have much more detailed information on the display’s behavior than the EDID can contain
So if you care about color accuracy, this is not a way out of getting a Colorimeter and profiling your display… but if you just have two screens and you’re annoyed that one of them has much more intense colors than the other, this option is an easy and fast way to fix it.
GamescopeJoshua Ashton implemented a new backend in gamescope, that uses Wayland subsurfaces to forward content to the host compositor instead of compositing it all into one image, and includes direct support for the frog_color_management_v1 protocol. The result of this is that with a new enough gamescope you don’t have to use any Vulkan layers to have gamescope pass HDR content to KWin, and you don’t have to use the --hdr-debug-force-output option anymore. If you want to play a game in HDR, you can now just put
gamescope -W 5120 -H 1440 --hdr-enabled --fullscreen %command%into its launch options in Steam (with width and height adjusted to your screen ofc) and you’re done.
The backend also reduces the image copies made in comparison to the previously default SDL backend; the game’s buffers are directly passed to the host compositor, in most cases even while overlays are visible.
GPU DriversThe driver situation is something I’ve been kind of ignoring in previous posts, but it’s obviously pretty important. First, let’s talk about the KMS API for setting a display into HDR mode. It consists of two parts:
- the HDR_OUTPUT_METADATA property, which compositors use to set mostly brightness related metadata about the image, like which transfer function is used and which brightness and color values the content roughly contains
- the Colorspace property, which compositors use to set the colorspace of the image, so that the display interprets the colors correctly
When you enable HDR in the system settings in Plasma, KWin will set HDR_OUTPUT_METADATA to the Perceptual Quantizer transfer function, and the brightness and mastering display properties to almost exactly what the display’s EDID says is optimal. This is done independently of the actual image content or what apps on the screen tell the compositor, to prevent the display from doing dumb things like dimming down just because an HDR video you opened claims it wants to display a supernova in your living room.
The one exception to that is the max_cll value - the maximum brightness of any pixel on the screen. It’s set to the maximum average brightness the display can show, because my Samsung C49RG94SSR monitor reduces the backlight brightness below SDR levels if you set the max_cll value it claims is ideal… With that one exception, this strategy has worked without issues so far.
On the Colorspace front, KWin sets the property to Default in SDR mode, and to BT2020_RGB when HDR is enabled. Sounds simple enough, but it’s of course actually more complicated.
Like any API that didn’t actually get used in practice for a very long time (if ever), both the API and the implementations were and are quite broken. The biggest issues I’ve seen so far are:
- AMD’s implementation of the Colorspace property for DisplayPort was broken, which caused colors to be washed out in HDR mode (fixed in Linux 6.8)
- the NVidia driver doesn’t force a modeset when HDR_OUTPUT_METADATA changes the transfer function or Colorspace changes its value, which causes temporary glitches when enabling HDR on some displays
- the Intel driver claims to support both properties for HDR laptop displays, but the implementation is missing entirely (this is being worked on)
- the Colorspace property implementations from Intel and NVidia cause washed out colors on many displays, because the API requires the compositor to change the property value depending on whether or not communication with the display uses RGB or YUV encoding… which the compositor doesn’t actually know anything about. The AMD implementation works around this by translating the property to the correct value in the kernel
- multiple laptop- or display specific issues that you can look up in the bug trackers if you want to
To summarize, if you want to use HDR, it’s best to use an AMD GPU with either kernel 6.8, or if you really must use an older kernel, HDMI. Even then, you might still see issues in some cases though - if you do, please make bug reports about it! Neither driver nor compositor developers can fix what they don’t know is broken.
What’s next?As always, for every bit of progress made or feature implemented, there’s ten more upcoming exciting things that could be talked about or worked on… but the big next topic is offloading of color management tasks to the GPU’s scanout hardware, to save power and improve performance. Next week I’ll be attending the 2024 Linux Display Next hackfest, which will focus on exactly that, so stay tuned!
Go Deh: Predicting results from small samples.
I've run simulations, tens of thousands of them at a time, over and over as we developed chips. In one project I noticed that I could predict the final result after only a small number of results were in which allowed me to halt the rest of the simulations, or make advance preparations for the final result.
I looked it up at the time and, indeed, there is an equation where if you want the pass rate of a "large" population to within a given accuracy, it will give you the minimum sample size to use.
To some, that's all gobledygook so I'll try to explain with some code.
ExplanationLets say you have a large randomised population of pass/fails or ones and zeroes:
from random import samplepopulation_size = 100_000 # must be large, > 65K?sample_size = 123 # Actualp_hat = 0.5 # Population pass rate, 0.5 == 50%
_ones = int(population_size*p_hat)_zeroes = population_size - _onespopulation = [1] * _ones + [0] * _zeroes
And that we take a sample from it and compute the pass rate of that single, smaller sample
pass_rate = (sum(random_sample()) # how many ones / sample_size) # convert to a pass rate
print(pass_rate) # e.g. 0.59027552674230146
Every time we run that pass_rate expression we get a different value. It is random. We need to run that pass_rate calculation many times to get an idea of what the likely pass_rate would be when using the given sample size:
runs_per_sample = 10_000 # each sample size is run this many times ~2500pass_rates = [sum(random_sample()) / sample_size for _ in range(runs_per_sample)]
I have learnt that the question to ask is not how many times the sample pass rate was the population pass rate, but to define an acceptable margin of error, (say 5%) and say we want to be confident that pass rates be within that margin a certain percentage of the time
epsilon = 0.05 # target margin of error in sample pass rate. 0.05 == +/-5%p_hat_min, p_hat_max = p_hat * (1 - epsilon), p_hat * (1 + epsilon)in_range_count = sum(p_hat_min <= pass_rate < p_hat_max for pass_rate in pass_rates)sample_confidence_level = in_range_count / runs_per_sampleprint(f"{sample_confidence_level = }") # = 0.4054
So for a sample size of 123 we could expect the pass rate of the sample to be within 5% of the actual pass rate of the population, 0.5 or 50%, onlr o.4 or 40% of the time!
We need more!What is actually done is we state what we think the population pass rate is, p_hat, (choose closer to 50% if you are unsure); the margin of error around p_hat we want, epsilon, usually +/-5% or +/-3%; and the confidence_level in hitting within the pass_rates margin of error.
There are calculators that will then give you n, the size of the sample needed to satisfy those condition.
Doing it myselfI calculated for one specific sample size, above. Obviousely, if I calculated pass_rates over a range of sample_sizes, and increqasing runs_per_sample, I could search out the sample size needed.
That is done in my next program. I have to switch to using the numpy library for its speed and sample_size becomes a range.
When the pass rate confidence levels are calculated I end up with a list of confidence levels for increasing sample sizes that are usually not increasing due to the randomness, e.g.
range_hits = [...0.94, 0.95, 0.93, 0.954, ... 0.95, 0.96, 0.95, 0.96, 0.96, 0.97, ...] # confidence levels
The range of sample_size corresponding to the first occurrence>= the requested confidence level, and the last occorrence of a confidence level < the requested confidence level in then slightly widened and the runs_per_sample increased on another iteration to get a better result.
Here's a sample of the output I get when searching
2013 <= n <= 2525 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(2013, 2694, 256), 500)
1501 <= n <= 2781 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1501, 3037, 256), 500)
1757 <= n <= 2013 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(221, 4061, 256), 500)
1714 <= n <= 1970 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1714, 2055, 128), 1000)
1458 <= n <= 2098 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1458, 2226, 128), 1000)
1586 <= n <= 1714 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(818, 2738, 128), 1000)
1564 <= n <= 1692 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1564, 1735, 64), 2000)
1500 <= n <= 1564 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1436, 1820, 64), 2000)
1553 <= n <= 1585 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1489, 1575, 32), 4000)
1547 <= n <= 1579 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1547, 1590, 16), 8000)
1547 <= n <= 1579 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1515, 1611, 16), 8000)
1541 <= n <= 1581 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1541, 1584, 8), 16000)
1501 <= n <= 1533 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1501, 1621, 8), 16000)
1501 <= n <= 1533 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1389, 1538, 8), 16000)
1503 <= n <= 1575 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1495, 1677, 8), 16000)
1503 <= n <= 1535 For p_hat, epsilon, confidence_level =(0.5, 0.05, 0.95) Using population_size, sample_size, runs_per_sample =(100000, range(1491, 1587, 4), 32000)
Use a sample n = 1535 to predict population pass rate of 50.0% +/-5% with a confidence level of 95%.
real 3m49.023suser 3m49.022ssys 0m1.361s
@author: paddy"""# %%from random import sampleimport numpy as np
def sample_search(population_size, sample_size, p_hat, epsilon, confidence_level, runs_per_sample) -> range: """ Arguments with example values: population_size = 100_000 # must be large, > 65K? sample_size = range(1400, 1750, 16) # (+min, +max, +step) p_hat = 0.5 # Population pass rate, 0.5 == 50% epsilon = 0.05 # target margin of error in sample pass rate. 0.05 == +/-5% confidence_level = 0.95 # sample to be within p_hat +/- epsilon, 0.95 == 95% of the time. runs_per_sample = 10_000 # each sample size is run this many times ~2500 Return: min,max range for the sample size, n, satisfying inputs. """ def create_1_0_array(population_size=100_000, p_hat=0.5) -> np.ndarray: "Create numpy array of ones and zeroes with p_hat% as ones" ones = int(population_size*p_hat + 0.5) array10 = np.zeros(population_size, dtype=np.uint8) array10[:ones] = 1 return array10
def rates_of_samples(population: np.ndarray, sample_size_range: range, runs_per_sample: int ) -> list[list[float]]: "Pass rates for range of sample sizes repeated runs_per_sample times." # many_samples_many_rates = [( np.random.shuffle(population), # shuffle every *run* # [population[:s_count].sum() / s_count # The pass rate for samples # for s_count in sample_size_range] # )[1] # drop the shuffle # for _ in range(runs_per_sample)] # Every run
many_samples_many_rates = [[ np.random.shuffle(population), # shuffle every *sample* [population[:s_count].sum() / s_count # The pass rate for samples for s_count in sample_size_range] ][1] # drop the shuffle for _ in range(runs_per_sample)] # Every run
return list(zip(*many_samples_many_rates)) # Transpose to by_sample_size_then_runs
population = create_1_0_array(population_size, p_hat) by_sample_size_then_runs = rates_of_samples(population, sample_size, runs_per_sample)
# Pass rates within target
target_pass_range = tmin, tmax = p_hat * (1 - epsilon), p_hat * (1 + epsilon) # Looking for rates within the range
range_hits = [sum(tmin <= sample_pass_rate < tmax for sample_pass_rate in single_sample_size) for single_sample_size in by_sample_size_then_runs] runs_for_confidence_level = confidence_level * runs_per_sample
for n_min, conf in zip(sample_size, range_hits): if conf >= runs_for_confidence_level: break else: n_min = sample_size.start
for n_max, conf in list(zip(sample_size, range_hits))[::-1]: if conf <= runs_for_confidence_level: n_max += sample_size.step # back a step break else: n_max = sample_size.stop if (n_min + sample_size.step) >= n_max and sample_size.step > 1: # Widen n_max = n_max + sample_size.step + 1
return range(n_min, n_max, sample_size.step)
def min_max_mid_step(from_range: range) -> tuple[int, int, float, int]: "Extract from **increasing** from_range the min, max, middle, step" mn, st = from_range.start, from_range.step # Handle range where start == stop mx = from_range.stop for mx in from_range: pass md = (mn + mx) / 2 return mn, mx, md, st def next_sample_size(new_samples, last_samples, runs_per_sample, widener=1.33 # Widen range by ): n_min, n_max, n_mid, n_step = min_max_mid_step(new_samples) l_min, l_max, l_mid, l_step = min_max_mid_step(last_samples) # Next range of samples computes in names with prefix s_ increase_runs = True if n_max == l_max: # Major expand of high end s_max = l_max + (l_max - l_min) increase_runs = False else: s_max = (n_mid +( n_max - n_mid)* widener)
if n_min == l_min: # Major expand of low end s_min = max(1, l_min + (l_min - l_max)) increase_runs = False else: s_min = (n_mid +( n_min - n_mid)* widener) s_min, s_max = (max(1, int(s_min)), int(s_max + 0.5)) s_step = n_step if s_min == s_max: if s_min > 2: s_min -= 1 s_max += 1 if increase_runs or n_max == n_min: runs_per_sample *= 2 if n_max == n_min: s_step = 1 else: s_step = max(1, (s_step + 1) // 2) # Go finer next_sample_range = range(max(1, int(s_min)), int(s_max + 0.5), s_step) return next_sample_range, runs_per_sample
# %%
if __name__ == '__main__':
population_size = 100_000 # must be large, > 65K? sample_size = range(50, 5_000, 512) # Increasing! p_hat = 0.50 # Population pass rate, 0.5 == 50% epsilon = 0.05 # target margin of error in sample pass rate. 0.05 == +/-5% confidence_level = 0.95 # sample to be within p_hat +/- epsilon, 0.95 == 95% of the time. runs_per_sample = 250 # each sample size is run this many time at start, ~250 max_runs_per_sample = 35_000
while runs_per_sample < max_runs_per_sample: new_range = sample_search(population_size, sample_size, p_hat, epsilon, confidence_level, runs_per_sample) n_min, n_max, n_mid, n_step = min_max_mid_step(new_range) print(f"{n_min} <= n <= {n_max}") print(f" For {p_hat, epsilon, confidence_level =}\n" f" Using {population_size, sample_size, runs_per_sample =}\n") sample_size, runs_per_sample = next_sample_size(new_range, sample_size, runs_per_sample)
print(f" Use a sample n = {n_max} to predict population pass rate of {p_hat*100.:.1f}% +/-{epsilon*100.:.0f}% " f"with a confidence level of {confidence_level*100.:.0f}%.")
END.