Rioran Tech Notes
41 subscribers
68 photos
1 file
53 links
Some notes on IT or tech staff I see along the way.
Download Telegram
I participated in my first LeetCode contest ever and ranked 1873 out of 33,261 participants, placing me in the top 6%
🔥5🤯1😱1
Here is how py2puml & plantuml tools combination could help you visualize your Python project entities.

Both are easily installed. PlantUML is even available as a PyCharm plugin. Normally going from zero to image would take less than a minute.

Minor hickups are possible though. On Windows py2puml tryed standard output instead of file creation. And output redirection was polluted with NULL symbols. Also by default py2puml didn't capture method names.

Here is my Kettle in Python:

class Heater:
def __init__(self, min_temperature: int = 0, max_temperature: int = 100):
self.enabled: bool = False
self.temperature: int = 0
self.min_temperature: int = min_temperature
self.max_temperature: int = max_temperature

def start(self):
self.enabled = True

def update(self):
if self.enabled:
self.temperature += 1
self.temperature = min(self.max_temperature, self.temperature)
else:
self.temperature -= 1
self.temperature = max(self.min_temperature, self.temperature)


class Reservoir:
def __init__(self, volume: int = 1000):
self.volume: int = volume
self.water: int = 0

def fill(self, input_water):
total_water = self.water + input_water
self.water = min(self.volume, self.water + input_water)
spilled = total_water - self.water

return spilled

def pour(self, amount):
pour_amount = min(self.water, amount)
self.water -= pour_amount

return pour_amount


class Kettle(Heater, Reservoir):
def __init__(self, volume=1000, min_temperature=0, max_temperature=100):
Reservoir.__init__(self, volume)
Heater.__init__(self, min_temperature, max_temperature)


That's what py2puml produced:

@startuml tea
!pragma useIntermediatePackages false

class tea.Heater {
enabled: bool
temperature: int
min_temperature: int
max_temperature: int
+ __init__(min_temperature: int = 0, max_temperature: int = 100)
+ start()
+ update()
}
class tea.Kettle {
}
class tea.Reservoir {
volume: int
water: int
+ __init__(volume: int = 1000)
+ fill(input_water: int): int
+ pour(amount: int): int
}
tea.Heater <|-- tea.Kettle
tea.Reservoir <|-- tea.Kettle
footer Generated by //py2puml//
@enduml
❤1👍1
Yo, check out an awesome package and project manager for #python !

https://docs.astral.sh/uv/

Easily installed on Windows even with:

pip install uv


Though take notice:

If installing from PyPI, we recommend installing uv into an isolated environment, e.g., with pipx


Here are some official documentation highlights:

🚀 A single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more.
⚡️ 10-100x faster than pip.
🗂 Provides comprehensive project management, with a universal lockfile.
❇️ Runs scripts, with support for inline dependency metadata.
🐍 Installs and manages Python versions.
🛠 Runs and installs tools published as Python packages.
🔩 Includes a pip-compatible interface for a performance boost with a familiar CLI.
🏢 Supports Cargo-style workspaces for scalable projects.
💾 Disk-space efficient, with a global cache for dependency deduplication.
⏬ Installable without Rust or Python via curl or pip.
🖥 Supports macOS, Linux, and Windows.

uv is backed by Astral, the creators of Ruff.
🔥1
Hey there! Here is a quick recap on asyncio Event Loop in #python

Asyncio itself is written in pure Python but depends on platform-specific C-functions.

Event Loop Policy is a global per-process object. Controls the Event Loop. Could be changed via API. Single event loop per thread by default.

Event Loop itself is an object too. Can be passed into different contexts. Has 2 stages: running or stopped. Relies on monotonic clock to avoid errors of going back in time (essentially the system's uptime).

Event loop collects coroutines, Tasks and Futures as handles. Assigns callbacks to these handles. Plans execution in a timed heap if possible. And checks for events needed by some handles.

Related links:

https://peps.python.org/pep-3153/ - Asynchronous IO support - precedes PEP 3156

https://peps.python.org/pep-3156/ - Asynchronous IO Support Rebooted: the “asyncio” Module - the key PEP which inspired this post

https://peps.python.org/pep-0418/#time-monotonic - Add monotonic time, performance counter, and process time functions - monotonic related link

https://peps.python.org/pep-3148/ - futures - execute computations asynchronously

https://peps.python.org/pep-0380/ - Syntax for Delegating to a Subgenerator (yield from)

https://peps.python.org/pep-0492/ - Coroutines with async and await syntax

https://github.com/python/cpython/blob/main/Lib/asyncio/base_events.py - source code containing the actual loop of Event Loop inside the method called "run_forever". Each iteration is processed inside the method "_run_once".

https://github.com/python/cpython/blob/main/Lib/selectors.py - key "select" method which crucial for collecting events

https://www.youtube.com/watch?v=RIVcqT2OGPA - AsyncIO and the Event Loop explained by ArjanCodes

https://www.youtube.com/watch?v=Ww2HBNpu98g - Demystifying AsyncIO: Building Your Own Event Loop in Python — Arthur Pastel
I like to examine top solutions to #leetcode problems if they are better than mine to learn something new. Here is a nice dynamic programming problem to find largest square area in a matrix of "0" and "1" strings. I was shocked to find a really short solution among leaders:

class Solution:
def maximalSquare(_, m):
s,a=[int(''.join(r),2) for r in m],0
while any(s):s,a=[a&b for a,b in pairwise([a&(a<<1) for a in s])],a+1
return a**2


Seems like magic! Way above my own solution. And it's really hard to understand how it works in current form, completely unreadable. So I start refactoring. First I apply basic Pythonic spacing and remove redundant function parameter:

from itertools import pairwise


class Solution:
@staticmethod
def maximalSquare(m):
s, a = [int(''.join(r), 2) for r in m], 0

while any(s):
s, a = [a & b for a, b in pairwise([a & (a << 1) for a in s])], a + 1

return a ** 2


Still no idea what makes this solution tick. Now let me disjoint multiple assignments, rename some variables...

from itertools import pairwise


class Solution:
@staticmethod
def maximalSquare(matrix):
matrix_rows_in_binary = [int(''.join(matrix_row), 2) for matrix_row in matrix]
top_square_side = 0

while any(matrix_rows_in_binary):
matrix_rows_in_binary = [
a & b
for a, b in pairwise([
binary_value & (binary_value << 1)
for binary_value in matrix_rows_in_binary
])
]
top_square_side += 1

return top_square_side ** 2


And now the first glimpse of understanding visits me! Author reduces each matrix row to just an integer. Pairwise function helps to reduce rows with some bitwise operations. Let me rename and reshuffle further:

from itertools import pairwise


class Solution:
@staticmethod
def maximalSquare(matrix):
matrix_rows_in_binary = [int(''.join(matrix_row), 2) for matrix_row in matrix]
top_square_side = 0

while any(matrix_rows_in_binary):
row_to_row_pairs = pairwise([value & (value << 1) for value in matrix_rows_in_binary])
matrix_rows_in_binary = [value & next_value for value, next_value in row_to_row_pairs]
top_square_side += 1

return top_square_side ** 2


And thus I learned a new technique. Pretty neat to reduce rows amount unless all rows are empty. Bitwise AND ensures we go unless square cant continue to grow.
🔥1
Forwarded from Roman Voronov
Today I had a technical interview using https://codepad.app/ editor. It is an awesome one! Fast, no delays, access to file system so you can model whole application. I didn't even need to log in. I used terminal to open Python shell to test a mechanism. Features are impressive and they claim they are free!
👍3
Forwarded from Roman Voronov
I published a concise yet diverse list of beginner-friendly and fun leetcode tasks. Which in my opinion are great for a start in any programming language. They are ordered from the easiest to not-so-easy. I often recommend those to my students and decided to share in a form of leetcode list.
I’m happy to share that I’ve obtained a new certification: Finished Computer Science course from CS50!

I nailed harder versions of every project quite fast. Though I really struggled with the idea of the final project and procrastinated for several months. But as my recorder hobby grew - I finally got inspired! As my final project I made a helper tool for me to withstand a daily challenge of playing melodies: FluTabs

Thanks to everyone involved for such a wonderful journey. David J. Malan & Harvard University. Problem sets were really fun and challenging! And the spirit of a course made myself better as a tutor.

Certificate link
❤2🔥1🎉1
From time to time I dive into small fun projects.

For example, a Python (PyGame + PyGbag) mini-game on cave exploration, playable on PC in browser.

Source code could be found here.

Features:

- WASD motion controls, long-press enabled
- ESC to quit
- Fog of war hides the map
- Player spreads light in a small radius
- Stepping into a new cave increases map scale
- Caves are auto-generated
👍3🔥1
Python 3.15 has a lot of cool features planned on October 2026, and one of them is the ability to use lazy imports.

Lazy import allows to defer the loading and execution of a module until the first time the imported name is used. It can reduce startup time and memory usage, especially for applications with huge dependency graphs.

lazy import pandas as pd
lazy from numpy import inf


The idea was first rejected through PEP-690 since it was intended as a default option which could lead to an unpredictable behavior. But it was later accepted through PEP-810 making lazy imports explicit.
👍1👀1
I read the article "A Heuristic Approach for a Real-World Electric Vehicle Routing Problem" by Mengting Zhao and Yuwei Lu, published in Algorithms (MDPI, ISSN 1999‑4893) on February 20, 2019: https://www.mdpi.com/1999-4893/12/2/45

The authors address an EVRP — the Electric Vehicle Routing Problem — using a hybrid approach that combines ALNS (Adaptive Large Neighborhood Search) with IP (Integer Programming). They begin by constructing a fast feasible initial solution that links pickup points, customers, and charging stations, and then refine it through several optimization stages.

Penalty functions enforce key constraints such as time windows, vehicle capacity, and battery capacity. A variety of removal and insertion heuristics help preserve strong subroutes while repeatedly improving weaker ones. To choose between similar candidate solutions, the algorithm uses a temperature‑based acceptance rule, allowing controlled exploration early on and more selective improvements later.

Solutions are evaluated using features such as arrival times, total pickup and delivery demand, and the maximum load after the last departure from the distribution center.

I would have loved to see the authors expand on why they chose 100 iterations per segment. It feels like a “magic number” — most likely an empirically tuned value balancing solution quality and computation time. A short explanation or sensitivity analysis would have made this design choice clearer.

I really appreciated how clearly the authors presented their methods in pseudocode. Since I’ve solved a similar scheduling problem before, it made the mechanics immediately intuitive and easy to follow.

I was really inspired to see that even a 7% reduction in operational cost is significant enough to justify developing a new algorithm. In my own experience, I’ve often focused more on speeding up algorithms rather than on the impact they create. This paper was a great reminder that in real-world optimization, even single‑digit improvements can translate into substantial value.
👍1👏1
I've just finished reading a paper "Exact Algorithms for Maximum Clique: A Computational Study" by Patrick Prosser published on Algorithms (MDPI, ISSN 1999‑4893) on November 19, 2012: https://www.mdpi.com/1999-4893/5/4/545

A clique is a complete subgraph of a graph. Or, in simple terms, imagine a party: a clique is a group of people where everyone knows everyone else.

The paper critically examines several Java implementations of maximum‑clique algorithms, arranged from simpler to more advanced: two versions of MC, MCQ, two versions of MCS, and BBMC.

All these algorithms rely on the idea of the graph's chromatic number - the minimum number of colors needed to color the vertices so that every edge connects vertices of different colors. The chromatic number provides an upper bound on the size of a maximum clique, since in a complete subgraph all vertices must have different colors.

The authors modify the original algorithms from the papers they analyze. While these changes look mostly cosmetic and are meant to standardize the implementations for comparison, in theory they could influence the results. Still, the effort to normalize the algorithms is inspiring.

I really appreciated the authors critical approach - they carefully analyze where each algorithm might fail due to specific implementation issues. They also do a great job evaluating the impact of different data structures, clearly distinguishing where a particular choice is crucial for performance and where it's mostly syntactic sugar.

I fully agree with the authors attitude in section 2.3.3: they emphasize the importance of explaining why a particular algorithm achieves a speedup. That's exactly the kind of reasoning I want to see more often in scientific papers.

I especially liked their method for generating test graphs: for a given set of vertices, each edge is included with a 50% probability.

Another new idea for me was pigeonhole sorting: you create ordered bins for the value range, fill them with counts of occurrences, and reconstruct the sorted sequence from the bins. The O(n+k) complexity can be very attractive in the right situations.

I also liked the idea of forcibly stopping a recursive function when a time limit is exceeded - I hadn't used that technique before. Not that I am a fan of recursive functions since in Python language that I use most of the times they are risky to use due to the default recursion stack limitations.
Аrticle "Alpha-Beta Pruning and Althöfer’s Pathology-Free Negamax Algorithm" by Ashraf M. Abdelbar published on Algorithms (MDPI, ISSN 1999‑4893) examines the correctness and compatibility of alpha–beta pruning with Althöfer’s modification of the negamax algorithm: https://www.mdpi.com/1999-4893/5/4/521

Negamax is a variant of the minimax algorithm designed for symmetric, zero-sum games. Minimax algorithms search for the best and worst moves in adversarial settings. Althöfer showed that the probability of mis-evaluating the root position decreases exponentially as the search depth increases. His idea - a modification of negamax - is resistant to minimax pathologies, namely the paradox where a deeper search can lead to a worse decision than a shallower one.

Alpha–beta pruning is an approach where threshold values (α and β) allow us to skip exploring branches that are guaranteed NOT to improve the final evaluation.

When reading the evaluation formula, I initially mistook the symbol "Γ" for a Cyrillic letter. In fact, it is the Greek capital gamma, the traditional notation for the neighborhood of a vertex in graph theory. Γ(x) typically denotes the set of neighbors of x. In the article, the same symbol is used for the set of successors of a game state x, which clearly reflects a graph-theoretic notation.

It is also interesting how the evaluation formula uses a sign flip. The minus indicates that when the turn switches, we evaluate the position from the opponent's perspective: our best move corresponds to their worst outcome.

In the pseudocode for alpha–beta pruning, I unexpectedly encountered the word "od" as the closing marker for a "do" loop. It turns out there is an entire old-school programming tradition of reversing block keywords for brevity and symmetry.

At the end of the article, the authors argue that Althöfer's idea adds almost no computational overhead. I think this is because Althöfer's adjustment to negamax is essentially a mathematical correction of the evaluation scheme. The modification preserves the structure of the search tree and does not introduce expensive operations.
Python 3: the story of hash and equality dunder methods

I). Python 3.0: a set of previously existed data model rules were enforced to define how objects should be hashed (1). The key idea is that if objects' values are equal, their hashes should also be equal. The reverse is not required - hash collisions are allowed. By default both hash and equality dunders were inherited from the object class, and the default hash is derived from the memory reference returned by id(self). If a user-defined class overrides only the equality method, than the hash method inherited from the object is automatically set to None.

II). Python 3.3: hash randomization becomes enabled by default (2). All thanks to a worldwide vulnerability that allowed DoS attacks on web servers through crafted hash collisions (3). From now on, str and bytes objects began using a randomized hash seed per process to make their hash values unpredictable across runs.

III). Python 3.4: previous change didn't fully address the DoS change, so the SipHash algorithm was deployed for string and bytes hashing (4). It's authors named it after "a short input PRF" (5). Where PRF stands for pseudo-random function. SipHash is cryptographically strong: it uses a secret 128‑bit key generated per process and mixes input data through a sequence of add‑rotate‑xor operations over 64‑bit words. Even if you know the algorithm inside out - good luck predicting collisions without knowing the secret key!

IV). Python 3.7: dataclasses arrive with their hashing automated (6). Depending on how dataclass input parameters "eq" and "frozen" are set, the dunder methods are either generated, inherited from the object, or set to None. This way the previously established rules are applied automatically on behalf of the developer.

V). Python 3.9: here come type-hinting generics in standard collections (7). Up to this point a type hint like "list[int]" was essentially a typing.List[int] object. But from now on it becomes an actual runtime object - types.GenericAlias, hashable and structurally comparable. Immutable, of course, in order to follow the rules.

VI). Python 3.12: new syntax for generic types introduces structural equality for type parameters and generic aliases (8). And if equality is defined but no consistent hashing exists, Python now sets hash dunder to None, rendering some type expressions - like T - unhashable.

VII). Python 3.13: huge expansion on 3.12 immortal objects (9). It matters due to a fact that immortal objects' never get deallocated, so their memory addresses remain fixed and never reused for the entire lifetime of the interpreter. This goes along with their hash values being permanently stable.

1 - Python docs https://docs.python.org/3.0/reference/datamodel.html#object.__hash__
2 - Python docs https://docs.python.org/3.3/reference/datamodel.html#object.__hash__
3 - GitHub issue discussion https://github.com/python/cpython/issues/57912
4 - PEP 456 https://peps.python.org/pep-0456
5 - PDF article https://cr.yp.to/siphash/siphash-20120918.pdf
6 - PEP 557 https://peps.python.org/pep-0557/
7 - PEP 585 https://peps.python.org/pep-0585/
8 - PEP 695 https://peps.python.org/pep-0695/
9 - PEP 683 https://peps.python.org/pep-0683/
Today I spent a few hours at the Institute for Informatics and Automation Problems (IIAP) with the CMEL - Computational Molecular Engineering Lab, site: https://www.cmellab.com/ The visit happened thanks to Hrachya Ishkhanyan, with whom I once discussed a Python program for the Armenian Code Academy =3 Thanks to Narek Aslikyan

This time I was joined by Armen, Eduard, and Hayk - marked on the photo - a team with a great sense of humor and impressive scientific depth!

CMEL focuses on fundamental research in molecular modeling and simulation. Their work could help pharmaceutical companies save significant time and money when developing new drugs.

One of the team's current directions is calculating the composition and characteristics of the "core" of drug conglomerates to identify stable structures. In simple terms: they compute molecular "capsules" whose hydrophobic parts face inward and hydrophilic parts face outward. The stability of such a composite capsule is achieved by selecting the right types and quantities of molecules whose positively and negatively charged regions alternate to form a stable lattice - a micelle.

Pharma companies then take this further by filling the capsule's interior with the required active compounds and engineering the outer "magic" needed for targeted delivery.

Some of the team's projects include analyzing stacking interactions of flat molecular fragments like benzene rings, computing the energies of complex molecules as their substructures rotate, and pre‑calculating optimal molecular parameters for subsequent simulations.

While reviewing plots where Hrachya compared their rotational‑energy model with empirical data, I noticed a curious pattern in graphs that initially looked like a mismatch. The peaks were similar in shape but shifted by suspiciously "round" angles for spatial rotations - 90 and 180 degrees. I pointed it out half‑jokingly, but Hrachya said this alone deserves a mention in a scientific paper =3 I’m delighted - and even offered to run the next paper through the sieve of my naïve questions.

Thanks for having me, would love to visit again sometime!
Forwarded from Roman Voronov
PEP 830 - in Python 3.15, exception objects will include nanosecond‑precision timestamps. The feature carries minimal risk and negligible overhead, and it remains disabled by default.
🔥2
I started the "Claude 101" course made by Anthropic Academy. Here are some chaotic notes along the way through the first 4 chapters combined under the "Meet Claude" title. Some of them come from my methodology and tutoring experience.

Chapter "Your first conversation with Claude" implies - never says that, but gives a feeling - that memory feature is always on: "Memory automatically saves key context from your conversations — your role, preferences, past decisions, and working style". But I had to turn it on manually in the Settings -> Capabilities -> Memory -> "Generate memory from chat history". Microsoft Copilot I use did it automatically, kudos to him there. And a good course should provide information on how to find those settings, had to navigate myself, UX/UI could be improved.

That immediately made me think: what version of Claude the course is for? May be info is just outdated? Hard to devise since I didn't find any versions or dates mentioned in the course credits. Only 2025 copyright.

I submitted 2 previous paragraphs to the course feedback.

Course tells about /resource/use-cases page. Cases are quite interesting, a lot to be inspired by.

By the way, the next chapter "Getting better results" explicitly addresses that a feature - in this case "web search" - might be enabled. Unlike the "memory" one.

Multiple chapters at this point suggest to start a fresh dialogue instead of trying to fix an old one gone wrong. Hm, might be a massive use case judging by info repetition.

And here I face a repetition of "4D Framework for AI Fluency" inventors introduction. I saw it in the previous chapter. Inventors might be important, but a good course should avoid facts duplication. Unless it supports a clear idea, tutoring intention. At this point I start to suspect that course was AI generated at least partially.

Current course used a video from "AI Fluency" course. So, I should prepare myself for more information duplication if I intend to go through all Anthropic courses.

I started to see a pattern. Course avoids showing specific usages as much as possible. It insists on the idea like "do -> observe -> adapt -> repeat". But this is exactly where actual tutoring value lies - show, don't tell. Well, I understand that 101 course should try to be slim enough. But earlier use cases page were advertised, might be a good idea to show more examples on how to adapt to AI limitations. Without asking to go through another course, a similar gallery would be great.

Chapter on desktop app dives right into the application usage. Though, without providing the link to download and install the app. Which would be quite convenient to have.

I installed the app for Windows. Well... the installation logo animation is awesome. But the fact that your window floats above all my applications and I cant move it or hide it is mildly disturbing.

I hesitated when I had to log in inside the application. I forgot what google account I used for a paid plan. Sadly, web interface I use often didn't give me any hints. Though desktop application, once I logged in, didn't have any problems showing me my mail.

Unlike Copilot, the Claude app had obvious ways to adjust the screen zoom. Kudos to Claude here.

Chapter focuses on a list of cool features. But it highlights every time that they are described for Mac. A good methodology is to separate platforms. A simple switch to show different info. Or at least a section for one platform first, and one for the other next.

I used shortcut to start messaging with the AI. But making it see my screen, as the training says is possible, was not trivial. And application itself proposed browser connector only, so "window sharing" feels like a stretch on the training part.

Scheduled tasks is a great feature! Haven't tried it yet, but sounds awesome.

Only in the second half it is finally mentioned that application is design with macOS priority in mind. Thus Windows might lack some features. This is important, you should open the chapter with this disclaimer.
Running Claude Code through application is somewhat fun, some new visuals are there compared to my VSCode terminal window. Key icons are from Mac, no matter my actual platform :D
🐳1
I just read an article by legendary Daniel Lemire titled "Only 17% of all 64-bit Integers are products of two 32-bit integers", and here's what was new for me:

I'd never really thought about how limited the space of multiplication results is. Granted, roughly a sixth of all 64-bit values is actually quite a lot - but it's good to be aware that you can only get so many distinct results out of two fixed-size factors.

I could have known this, but I'm still amazed by how the achievable space of multiplication results shrinks as the factor size grows. Two effects compound: a larger share of the 2n-bit range consists of values with no valid factorization into two n-bit factors (every prime in the upper range, for instance), and collisions pile up as different factor pairs land on the same product. Erdos proved this reachable fraction tends to zero as n grows.

What I didn't expect was that the 30- and 32-bit cases could be pinned down with an exact count rather than just estimated. Webster and colleagues built an algorithm that scales the exact computation far beyond what brute force can reach - yielding a precise figure of 3,215,709,724,700,470,902 reachable values for the 32-bit case.

I also learned that the Monte Carlo method could be used to approximate the space of achievable products. I'd previously only known it for approximating π, so this gave me a nice wow moment.

I was also mildly surprised to see Lemire use single-character variables instead of full words in a Python snippet. I understand this is a traditional scientific convention, but I'd still love to see academic code adopt more of the readability practices common in commercial work. Readability matters everywhere.

Just for fun, and to better understand Lemire's snippet, I rewrote it in a more production-style Python with a minor optimization (an early exit once a candidate exceeds the limit). Here is the original code:

for p in factor_multiplicities:
new_candidates = []
for c in candidates:
for i in range(factor_multiplicities[p] + 1):
if c * (p ** i) < 2**32:
new_candidates.append(c * (p ** i))
for new_c in new_candidates:
candidates.append(new_c)
m = max(candidates)
print(f"Maximum candidate: {m}")
leftover = n // m
print(f"Leftover: {leftover}")
if leftover >= 2**32:
print("Leftover is too large, cannot find a suitable candidate.")


And here is mine:

INT32_LIMIT = 2 ** 32


for prime, multiplicity in factor_multiplicities.items():
new_candidates = []

for candidate in divisor_candidates:
scaled_candidate = candidate

for _ in range(multiplicity):
scaled_candidate *= prime

if scaled_candidate >= INT32_LIMIT:
break

new_candidates.append(scaled_candidate)

divisor_candidates.extend(new_candidates)

max_candidate = max(divisor_candidates)
print(f"Maximum candidate: {max_candidate}")

leftover = product // max_candidate
print(f"Leftover: {leftover}")

if leftover >= INT32_LIMIT:
print("Leftover is too large, cannot find a suitable candidate.")
🔥1👀1