This stems purely from some play on my part. Suppose I asked you to work with the sequence formed as 2*n*F_n + 1, where F_n is the n'th Fibonacci number? Part of me would not be surprised to find there is nothing simple we could do. But, then it costs nothing to try, to see where MATLAB can take me in an explorative sense.
n = sym(0:100).';
Fn = fibonacci(n);
Sn = 2*n.*Fn + 1;
Sn(1:10) % A few elements
ans = 
For kicks, I tried asking ChatGPT. Giving it nothing more than the first 20 members of thse sequence as integers, it decided this is a Perrin sequence, and gave me a recurrence relation, but one that is in fact incorrect. Good effort from the Ai, but a fail in the end.
Is there anything I can do? Try null! (Look carefully at the array generated by Toeplitz. It is at least a pretty way to generate the matrix I needed.)
X = toeplitz(Sn,[1,zeros(1,4)]);
rank(X(5:end,:))
ans = 5
Hmm. So there is no linear combination of those columns that yields all zeros, since the resulting matrix was full rank.
X = toeplitz(Sn,[1,zeros(1,5)]);
rank(X(6:end,:))
ans = 5
But if I take it one step further, we see the above matrix is now rank deficient. What does that tell me? It says there is some simple linear combination of the columns of X(6:end,:) that always yields zero. The previous test tells me there is no shorter constant coefficient recurrence releation, using fewer terms.
null(X(6:end,:))
ans = 
Let me explain what those coefficients tell me. In fact, they yield a very nice recurrence relation for the sequence S_n, not unlike the original Fibonacci sequence it was based upon.
S(n+1) = 3*S(n) - S_(n-1) - 3*S(n-2) + S(n-3) + S(n-4)
where the first 5 members of that sequence are given as [1 3 5 13 25]. So a 6 term linear constant coefficient recurrence relation. If it reminds you of the generating relation for the Fibonacci sequence, that is good, because it should. (Remember I started the sequence at n==0, IF you decide to test it out.) We can test it out, like this:
SfunM = memoize(@(N) Sfun(N));
SfunM(25)
ans = 3751251
2*25*fibonacci(sym(25)) + 1
ans = 
3751251
And indeed, it works as expected.
function Sn = Sfun(n)
switch n
case 0
Sn = 1;
case 1
Sn = 3;
case 2
Sn = 5;
case 3
Sn = 13;
case 4
Sn = 25;
otherwise
Sn = Sfun(n-5) + Sfun(n-4) - 3*Sfun(n-3) - Sfun(n-2) +3*Sfun(n-1);
end
end
A beauty of this, is I started from nothing but a sequence of integers, derived from an expression where I had no rational expectation of finding a formula, and out drops something pretty. I might call this explorational mathematics.
The next step of course is to go in the other direction. That is, given the derived recurrence relation, if I substitute the formula for S_n in terms of the Fibonacci numbers, can I prove it is valid in general? (Yes.) After all, without some proof, it may fail for n larger than 100. (I'm not sure how much I can cram into a single discussion, so I'll stop at this point for now. If I see interest in the ideas here, I can proceed further. For example, what was I doing with that sequence in the first place? And of course, can I prove the relation is valid? Can I do so using MATLAB?)
(I'll be honest, starting from scratch, I'm not sure it would have been obvious to find that relation, so null was hugely useful here.)
Independent researcher: Nguyễn Khánh Tùng
ORCID: 0009-0002-9877-4137
Email: traiphieu.com@gmail.com
The NKTg Law (Law of Variable Inertia) not only holds value in physics but also opens up wide possibilities for applications in programming and simulation. The remarkable point here is that the same law, the same formula, can be implemented across a wide range of different programming languages.
In the content below, you will find a collection of 150 code snippets, each corresponding to one of the world’s leading programming languages:
Python, C++, Java, C, C#, JavaScript, TypeScript, PHP, Ruby, Swift, Go, Rust, Kotlin, Dart, Scala, R, MATLAB, Julia, Haskell, Perl, Shell, SQL, Visual Basic, Assembly, Ada, Fortran, Prolog, Scheme, Lisp, Scratch, Smalltalk, Pascal, Groovy, PowerShell, Apex, ABAP, ActionScript, Algol, Alice, AmbientTalk, AngelScript, APL, Arc, Arduino, ASP.NET, AssemblyScript, ATS, AWK, Ballerina, BASIC, VHDL, Verilog, Assembly, AutoHotkey, AutoLISP, AWK, Bash, bc, Boo, Clojure, COBOL, Common Lisp, Crystal, D, Delphi/Object Pascal, Dylan, Eiffel, Elixir, Elm, Emacs Lisp, Erlang, F#, Factor, Falcon, Fantom, Felix, Forth, Fortress, Frink, Gambas, GAMS, GAP, Genie, GLSL, Hack, Haxe, HDL, HLSL, Hope, HTML, HyperTalk, Icon, IDL, Inform, Io, Ioke, J, J#, JScript, JavaFX Script, Io, Ioke, J, J#, JScript, Julia, Kotlin, LabVIEW, Ladder Logic, Lasso, Lava, Lisp, LiveCode, Logo, Lua, M4, Magik, Maple, Mathematica, MATLAB, Mercury, Modula-2, Modula-3, MoonScript, Nemerle, NetLogo, Nim, Nix, Objective-C, Objective-J, OCaml, OpenCL, OpenEdge ABL, Oz, PL/I, PL/SQL, PostScript, Promela, Pure, Q#, Racket, RAPID, REBOL, Red, Rexx, Ring, Solidity, SPARK, SPSS, Squirre
All the code snippets illustrate how to calculate the fundamental quantities of The NKTg Law on Varying Inertia:
The movement tendency of an object in space depends on the relationship between its position, velocity, and mass.
NKTg = f(x, v, m)
In which:
  • x is the position or displacement of the object relative to the reference point.
  • v is the velocity.
  • m is the mass.
The movement tendency of the object is determined by the following basic product quantities:
NKTg₁ = x × p
NKTg₂ = (dm/dt) × p
In which:
  • p is the linear momentum, calculated by p = m × v.
  • dm/dt is the rate of mass change over time.
  • NKTg₁ is the quantity representing the product of position and momentum.
  • NKTg₂ is the quantity representing the product of mass variation and momentum.
  • The unit of measurement is NKTm, representing a unit of varying inertia.
The sign and value of the two quantities NKTg₁ and NKTg₂ determine the movement tendency:
  • If NKTg₁ is positive, the object tends to move away from the stable state.
  • If NKTg₁ is negative, the object tends to move toward the stable state.
  • If NKTg₂ is positive, the mass variation has a supporting effect on the movement.
  • If NKTg₂ is negative, the mass variation has a resisting effect on the movement.
The stable state in this law is understood as the state in which the position (x), velocity (v), and mass (m) of the object interact with each other to maintain the movement structure, helping the object avoid losing control and preserving its inherent movement pattern.
# Python:
versatile, easy to learn, strong for AI and data science
x, v, m, dm_dt = 2.0, 3.0, 5.0, 0.1
p = m * v
NKTg1 = x * p
NKTg2 = dm_dt * p
print(f"p={p}, NKTg1={NKTg1}, NKTg2={NKTg2}")
Java
// Java: enterprise applications, Android
public class NKTgLaw {
public static void main(String[] args) {
double x=2, v=3, m=5, dm_dt=0.1;
double p = m*v, NKTg1 = x*p, NKTg2 = dm_dt*p;
System.out.printf(
"p=%.2f NKTg1=%.2f NKTg2=%.2f%n", p, NKTg1, NKTg2);
}
}
Implementing the same law across 150 programming ecosystems demonstrates its universality and flexibility, while also confirming that any language—whether general-purpose and popular, or specialized and classical—can apply the NKTg Law to simulate, analyze, and handle practical problems.
Full list of 150 programming languages (complete) — due to post size limits I placed the complete list on an external page for easy viewing and download:
You can refer to the following four related articles to gain a deeper understanding of the NKTg Law and its applications
I saw an interesting problem on a reddit math forum today. The question was to find a number (x) as close as possible to r=3.6, but the requirement is that both x and 1/x be representable in a finite number of decimal places.
The problem of course is that 3.6 = 18/5. And the problem with 18/5 has an inverse 5/18, which will not have a finite representation in decimal form.
In order for a number and its inverse to both be representable in a finite number of decimal places (using base 10) we must have it be of the form 2^p*5^q, where p and q are integer, but may be either positive or negative. If that is not clear to you intuitively, suppose we have a form
2^p*5^-q
where p and q are both positive. All you need do is multiply that number by 10^q. All this does is shift the decimal point since you are just myltiplying by powers of 10. But now the result is
2^(p+q)
and that is clearly an integer, so the original number could be represented using a finite number of digits as a decimal. The same general idea would apply if p was negative, or if both of them were negative exponents.
Now, to return to the problem at hand... We can obviously adjust the number r to be 20/5 = 4, or 16/5 = 3.2. In both cases, since the fraction is now of the desired form, we are happy. But neither of them is really close to 3.6. My goal will be to find a better approximation, but hopefully, I can avoid a horrendous amount of trial and error. It would seem the trick might be to take logs, to get us closer to a solution. That is, suppose I take logs, to the base 2?
log2(3.6)
ans = 1.8480
I used log2 here because that makes the problem a little simpler, since log2(2^p)=p. Therefore we want to find a pair of integers (p,q) such that
log2(3.6) + delta = p + log2(5)*q
where delta is as close to zero as possible. Thus delta is the error in our approximation to 3.6. And since we are working in logs, delta can be viewed as a proportional error term. Again, p and q may be any integers, either positive or negative. The two cases we have seen already have (p,q) = (2,0), and (4,-1).
Do you see the general idea? The line we have is of the form
log2(3.6) = p + log2(5)*q
it represents a line in the (p,q) plane, and we want to find a point on the integer lattice (p,q) where the line passes as closely as possible.
[Xl,Yl] = meshgrid([-10:10]);
plot(Xl,Yl,'k.')
hold on
fimplicit(@(p,q) -log2(3.6) + p + log2(5)*q,[-10,10,-10,10],'g-')
plot([2 4],[0,-1],'ro')
hold off
Now, some might think in terms of orthogonal distance to the line, but really, we want the vertical distance to be minimized. Again, minimize abs(delta) in the equation:
log2(3.6) + delta = p + log2(5)*q
where p and q are integer.
Can we do that using MATLAB? The skill about about mathematics often lies in formulating a word problem, and then turning the word problem into a problem of mathematics that we know how to solve. We are almost there now. I next want to formulate this into a problem that intlinprog can solve. The problem at first is intlinprog cannot handle absolute value constraints. And the trick there is to employ slack variables, a terribly useful tool to emply on this class of problem.
Rewrite deta as:
delta = Dpos - Dneg
where Dpos and Dneg are real variables, but both are constrained to be positive.
prob = optimproblem;
p = optimvar('p',lower = -50,upper = 50,type = 'integer');
q = optimvar('q',lower = -50,upper = 50,type = 'integer');
Dpos = optimvar('Dpos',lower = 0);
Dneg = optimvar('Dneg',lower = 0);
Our goal for the ILP solver will be to minimize Dpos + Dneg now. But since they must both be positive, it solves the min absolute value objective. One of them will always be zero.
r = 3.6;
prob.Constraints = log2(r) + Dpos - Dneg == p + log2(5)*q;
prob.Objective = Dpos + Dneg;
The solve is now a simple one. I'll tell it to use intlinprog, even though it would probably figure that out by itself. (Note: if I do not tell solve which solver to use, it does use intlinprog. But it also finds the correct solution when I told it to use GA offline.)
solve(prob,solver = 'intlinprog')
Solving problem using intlinprog. Running HiGHS 1.7.1: Copyright (c) 2024 HiGHS under MIT licence terms Coefficient ranges: Matrix [1e+00, 2e+00] Cost [1e+00, 1e+00] Bound [5e+01, 5e+01] RHS [2e+00, 2e+00] Presolving model 1 rows, 4 cols, 4 nonzeros 0s 1 rows, 4 cols, 4 nonzeros 0s Solving MIP model with: 1 rows 4 cols (0 binary, 2 integer, 0 implied int., 2 continuous) 4 nonzeros Nodes | B&B Tree | Objective Bounds | Dynamic Constraints | Work Proc. InQueue | Leaves Expl. | BestBound BestSol Gap | Cuts InLp Confl. | LpIters Time 0 0 0 0.00% 0 inf inf 0 0 0 0 0.0s R 0 0 0 0.00% 0 0.765578819 100.00% 0 0 0 1 0.0s H 0 0 0 0.00% 0 0.5905649912 100.00% 11 5 0 6 0.0s H 0 0 0 0.00% 0 0.2686368963 100.00% 12 5 1 6 0.0s H 0 0 0 0.00% 0 0.0875069139 100.00% 13 5 1 6 0.0s H 0 0 0 0.00% 0 0.0532911986 100.00% 14 5 1 6 0.0s H 0 0 0 0.00% 0 0.0190754832 100.00% 15 5 6 6 0.0s H 0 0 0 0.00% 0 0.0151402321 100.00% 16 5 11 6 0.0s H 0 0 0 0.00% 0 0.00115357525 100.00% 17 5 22 6 0.0s Solving report Status Optimal Primal bound 0.00115357524726 Dual bound 0.00115357524726 Gap 0% (tolerance: 0.01%) Solution status feasible 0.00115357524726 (objective) 0 (bound viol.) 0 (int. viol.) 0 (row viol.) Timing 0.01 (total) 0.00 (presolve) 0.00 (postsolve) Nodes 1 LP iterations 98 (total) 1 (strong br.) 6 (separation) 88 (heuristics) Optimal solution found. Intlinprog stopped at the root node because the objective value is within a gap tolerance of the optimal value, options.AbsoluteGapTolerance = 1e-06. The intcon variables are integer within tolerance, options.ConstraintTolerance = 1e-06.
ans = struct with fields:
Dneg: 0 Dpos: 0.0012 p: 39 q: -16
The solution it finds within the bounds of +/- 50 for both p and q seems pretty good. Note that Dpos and Dneg are pretty close to zero.
2^39*5^-16
ans = 3.6029
and while 3.6028979... seems like nothing special, in fact, it is of the form we want.
R = sym(2)^39*sym(5)^-16
R = 
vpa(R,100)
ans = 
3.6028797018963968
vpa(1/R,100)
ans = 
0.277555756156289135105907917022705078125
both of those numbers are exact. If I wanted to find a better approximation to 3.6, all I need do is extend the bounds on p and q. And we can use the same solution approch for any floating point number.
Independent researcher: Nguyễn Khánh Tùng
ORCID: 0009-0002-9877-4137
Email: traiphieu.com@gmail.com
Abstract
Every fundamental law of physics has a characteristic quantity and a unit of measurement (e.g., Newton for force, Joule for energy). The NKTg Law (Law of Varying Inertia) introduces a new physical quantity — varying inertia — defined by the interaction between position, velocity, and mass.
To measure this new quantity, I propose the NKTm unit, verified with NASA JPL Horizons data (Neptune, 2023–2024). Results indicate that NKTm is an independent fundamental unit, comparable in significance to Newton, Pascal, Joule, and Watt, with applications in astronomy, aerospace, and engineering.
This article clarifies the measurement unit of the NKTg Law (NKTm) and highlights its applications, many of which I have already implemented and shared as code examples on MATLAB Central.
1. Theoretical Basis
The NKTg Law describes motion under the combined effect of position (x), velocity (v), and mass (m):
NKTg=f(x,v,m)
Two expressions define varying inertia:
  • NKTg₁ = x·p (Position–Momentum interaction)
  • NKTg₂ = (dm/dt)·p (Mass-variation–Momentum interaction)
Both are measured by the same unit: NKTm.2. Dimensional Analysis
  • From NKTg₁: [ML2/T][M·L²/T][ML2/T]
  • From NKTg₂: [M2L/T2][M²·L/T²][M2L/T2]
Thus, NKTm is a unique unit that can take different dimensional forms depending on which component dominates.
For comparison:
QuantityUnitDimensionForceNewton (N)[M·L/T²]EnergyJoule (J)[M·L²/T²]PowerWatt (W)[M·L²/T³]Varying inertia (NKTg₁)NKTm[M·L²/T]Varying inertia (NKTg₂)NKTm[M²·L/T²]
3. Verification with NASA Data (Neptune, 2023–2024)
  • Position (x): 4.498×1094.498 \times 10^94.498×109 km
  • Velocity (v): 5.43 km/s
  • Mass (m): 1.0243×10261.0243 \times 10^{26}1.0243×1026 kg
  • Momentum (p = m·v): 5.564×10265.564 \times 10^{26}5.564×1026 kg·m/s
Results:
  • NKTg₁ = x·p ≈ 2.503 × 10³⁶ NKTm
  • NKTg₂ ≈ -1.113 × 10²² NKTm (assumed micro gas escape)
  • Total NKTg ≈ 2.501 × 10³⁶ NKTm
4. Applications
  • Astronomy: describe planetary mass variation, star/galaxy formation, and long-term orbital stability.
  • Aerospace: optimize rocket fuel usage, account for mass leakage, design ion/plasma engines.
  • Earth sciences: analyze GRACE-FO data, model ice melting, sea-level rise, and mass redistribution.
  • Engineering: variable-mass robotics, cargo systems, vibration analysis, fluid/particle simulations.
👉 Many of these applications are already available as MATLAB code examples that I have uploaded to MATLAB Central, showing how NKTm can be computed and applied in practice.5. Scientific Significance
  • Establishes a new fundamental unit (NKTm), independent of Newton and Joule.
  • Provides a theoretical framework for variable-mass dynamics, beyond Newton and Einstein.
  • Supports accurate computation and simulation of real-world systems with mass variation.
Conclusion
The introduction of the NKTm unit demonstrates that varying inertia is a measurable, independent physical quantity. Like Newton or Joule, NKTm lays the foundation for a new reference system in physics, with applications ranging from planetary mechanics to modern space technology.
This article not only clarifies the measurement standard of the NKTg Law, but also connects directly with practical MATLAB implementations for simulation and verification.
Discussion prompt:
What do you think about introducing a new physical unit like NKTm? Could it be integrated into MATLAB-based simulation frameworks for variable-mass systems?
You can refer to the following four related articles to gain a deeper understanding of the NKTg Law and its applications
Inertia: A Millennia-Long Journey
Inertia, the ability to maintain an object’s motion, has existed since ancient times. Aristotle viewed motion as an inherent property; an object stops only when the force disappears. For thousands of years, inertia remained an abstract concept, impossible to measure.
Galileo and Newton initiated a revolutionary leap. Newton defined inertia through the first law, but humans could only measure mass indirectly. In modern physics, inertia exists more in theory than in experiment; it is still not directly quantifiable.
NKTg Law: A Great Leap in Quantifying Inertia
NKTg Law – the Law of Varying Inertia allows for the measurement of inertia. Inertia becomes a variable quantity, depending on position, velocity, and mass:
NKTg=f(x,v,m)
NKTg₁ = x × p
NKTg₂ = (dm/dt) × p
With the NKTm unit, inertia becomes a measurable entity, both theoretical and practical. NKTm is the bridge between classical thinking and modern experimentation, enabling simulation, calculation, and deployment across all physical and engineering systems.
NKTg Law is implemented in 150 leading programming languages, from Python, C++, Java, MATLAB, R, Swift, Go to PL/I, PL/SQL, ASP.NET, Assembly. This enables:
  • Support for all software ecosystems, from desktop to web and mobile.
  • Direct integration with inertia-measuring sensors.
  • Simulation of objects from fundamental particles to planets and galaxies on a unified algorithmic platform.
Core Library & API: Global Knowledge of Inertia
Another historic advancement is the implementation of NKTg Law in 150 leading programming languages. From Python, C++, Java, MATLAB, R, Lua, Swift, Go to rarer languages like PL/I, PL/SQL, ASP.NET, Assembly, or COBOL, NKTg Law becomes a common language for modern simulation and computation systems.
This wide deployment allows:
  • Support for all software ecosystems, from desktop, server, to web and mobile.
  • Direct integration with sensors to measure inertia experimentally.
  • Easy simulation of objects, from fundamental particles to planets and galaxies, on the same algorithmic platform.
This is supported by the Core library & API of NKTg Law on GitHub: https://github.com/NKTgLaw/NKTgLaw, which provides:
  • Core implementation: core algorithms for calculating varying inertia,
  • REST/gRPC API: access to inertia data and system integration,
  • 150+ client wrappers: deployment support for over 150 programming languages, from infrastructure to application, from physics simulation to robotics, aviation, and astronomy.
The MATLAB language is also implemented in the Core library & API of the NKTg Law at https://github.com/NKTgLaw/NKTgLaw/tree/main/clients/matlab.
Thus, NKTg Law becomes a global digital science platform, where inertia is no longer a theoretical concept but data that can be analyzed, shared, and applied instantly.
Historical Significance and Cosmic Vision
Quantifying inertia is a milestone of knowledge, leading humanity into a new era of understanding the universe. With NKTg Law, NKTm, 150 programming languages, and sensor systems:
  • Inertia can be measured directly, no longer an abstract unknown.
  • All motion models – from elementary particles to galaxies – can be simulated and predicted accurately.
  • Understanding of nature and the universe enters a new era, where intrinsic properties of objects become scientific data.
Inertia, once theoretical, is now a numerical entity, opening doors for humanity to explore and understand the universe more deeply.
Conclusion
From Aristotle to Newton and modern physics, inertia has always been an abstract concept, not directly measurable. Thanks to NKTg Law and NKTm, along with 150 programming languages and sensor devices, inertia is now a practical measurable quantity, ushering in a new era of universal understanding, transforming physical knowledge from theory to data, from abstraction to measurement, from reasoning to discovery.
Since R2024b, a Levenberg–Marquardt solver (TrainingOptionsLM) was introduced. The built‑in function trainnet now accepts training options via the trainingOptions function (https://www.mathworks.com/help/deeplearning/ref/trainingoptions.html#bu59f0q-2) and supports the LM algorithm. I have been curious how to use it in deep learning, and the official documentation has not provided a concrete usage example so far. Below I give a simple example to illustrate how to use this LM algorithm to optimize a small number of learnable parameters.
For example, consider the nonlinear function:
y_hat = @(a,t) a(1)*(t/100) + a(2)*(t/100).^2 + a(3)*(t/100).^3 + a(4)*(t/100).^4;
It represents a curve. Given 100 matching points (t → y_hat), we want to use least squares to estimate the four parameters a1​–a4​.
t = (1:100)';
y_hat = @(a,t)a(1)*(t/100) + a(2)*(t/100).^2 + a(3)*(t/100).^3 + a(4)*(t/100).^4;
x_true = [ 20 ; 10 ; 1 ; 50 ];
y_true = y_hat(x_true,t);
plot(t,y_true,'o-')
  • Using the traditional lsqcurvefit-wrapped "Levenberg–Marquardt" algorithm:
x_guess = [ 5 ; 2 ; 0.2 ; -10 ];
options = optimoptions("lsqcurvefit",Algorithm="levenberg-marquardt",MaxFunctionEvaluations=800);
[x,resnorm,residual,exitflag] = lsqcurvefit(y_hat,x_guess,t,y_true,-50*ones(4,1),60*ones(4,1),options);
Local minimum found. Optimization completed because the size of the gradient is less than 1e-4 times the value of the function tolerance.
x,resnorm,exitflag
x = 4×1
20.0000 10.0000 1.0000 50.0000
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
resnorm = 9.7325e-20
exitflag = 1
  • Using the deep-learning-wrapped "Levenberg–Marquardt" algorithm:
options = trainingOptions("lm", ...
InitialDampingFactor=0.002, ...
MaxDampingFactor=1e9, ...
DampingIncreaseFactor=12, ...
DampingDecreaseFactor=0.2,...
GradientTolerance=1e-6, ...
StepTolerance=1e-6,...
Plots="training-progress");
numFeatures = 1;
layers = [featureInputLayer(numFeatures,'Name','input')
fitCurveLayer(Name='fitCurve')];
net = dlnetwork(layers);
XData = dlarray(t);
YData = dlarray(y_true);
netTrained = trainnet(XData,YData,net,"mse",options);
Iteration TimeElapsed TrainingLoss GradientNorm StepNorm _________ ___________ ____________ ____________ ________ 1 00:00:03 0.35754 0.053592 39.649
Warning: Error occurred while executing the listener callback for event LogUpdate defined for class deep.internal.train.SerialMetricManager:
Error using matlab.internal.capability.Capability.require (line 94)
This functionality is not available on remote platforms.

Error in matlab.ui.internal.uifigureImpl (line 33)
Capability.require(Capability.WebWindow);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in uifigure (line 34)
window = matlab.ui.internal.uifigureImpl(false, varargin{:});
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deepmonitor.internal.DLTMonitorView/createGUIComponents (line 167)
this.Figure = uifigure("Tag", "DEEPMONITOR_UIFIGURE");
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deepmonitor.internal.DLTMonitorView (line 123)
this.createGUIComponents();
^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deepmonitor.internal.DLTMonitorFactory/createStandaloneView (line 8)
view = deepmonitor.internal.DLTMonitorView(model, this);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.TrainingProgressMonitor/set.Visible (line 224)
this.View = this.Factory.createStandaloneView(this.Model);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.MonitorConfiguration/updateMonitor (line 173)
monitor.Visible = true;
^^^^^^^^^^^^^^^
Error in deep.internal.train.MonitorConfiguration>@(logger,evtData)weakThis.Handle.updateMonitor(evtData,visible) (line 154)
this.Listeners{end+1} = listener(logger,'LogUpdate',@(logger,evtData) weakThis.Handle.updateMonitor(evtData,visible));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.SerialMetricManager/notifyLogUpdate (line 28)
notify(this,'LogUpdate',eventData);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.MetricManager/evaluateMetricsAndSendLogUpdate (line 177)
notifyLogUpdate(this, logUpdateEventData);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.setupTrainnet>iEvaluateMetricsAndSendLogUpdate (line 140)
evaluateMetricsAndSendLogUpdate(metricManager, evtData);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.setupTrainnet>@(source,evtData)iEvaluateMetricsAndSendLogUpdate(source,evtData,metricManager) (line 125)
addlistener(trainer,'IterationEnd',@(source,evtData) iEvaluateMetricsAndSendLogUpdate(source,evtData,metricManager));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.BatchTrainer/notifyIterationAndEpochEnd (line 189)
notify(trainer,'IterationEnd',data);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.FullBatchTrainer/computeBatchTraining (line 112)
notifyIterationAndEpochEnd(trainer, matlab.lang.internal.move(data));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.BatchTrainer/computeTraining (line 144)
net = computeBatchTraining(trainer, net, mbq);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.Trainer/train (line 67)
net = computeTraining(trainer, net, mbq);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in deep.internal.train.train (line 30)
net = train(trainer, net, mbq);
^^^^^^^^^^^^^^^^^^^^^^^^
Error in trainnet (line 51)
[varargout{1:nargout}] = deep.internal.train.train(mbq, net, loss, options, ...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in LiveEditorEvaluationHelperEeditorId (line 27)
netTrained = trainnet(XData,YData,net,"mse",options);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in connector.internal.fevalMatlab

Error in connector.internal.fevalJSON
7 00:00:04 5.3382e-10 1.4371e-07 0.43992 Training stopped: Gradient tolerance reached
netTrained.Layers(2)
ans =
fitCurveLayer with properties: Name: 'fitCurve' Learnable Parameters a1: 20.0007 a2: 9.9957 a3: 1.0072 a4: 49.9962 State Parameters No properties. Use properties method to see a list of all properties.
classdef fitCurveLayer < nnet.layer.Layer ...
& nnet.layer.Acceleratable
% Example custom SReLU layer.
properties (Learnable)
% Layer learnable parameters
a1
a2
a3
a4
end
methods
function layer = fitCurveLayer(args)
arguments
args.Name = "lm_fit";
end
% Set layer name.
layer.Name = args.Name;
% Set layer description.
layer.Description = "fit curve layer";
end
function layer = initialize(layer,~)
% layer = initialize(layer,layout) initializes the layer
% learnable parameters using the specified input layout.
if isempty(layer.a1)
layer.a1 = rand();
end
if isempty(layer.a2)
layer.a2 = rand();
end
if isempty(layer.a3)
layer.a3 = rand();
end
if isempty(layer.a4)
layer.a4 = rand();
end
end
function Y = predict(layer, X)
% Y = predict(layer, X) forwards the input data X through the
% layer and outputs the result Y.
% Y = layer.a1.*exp(-X./layer.a2) + layer.a3.*X.*exp(-X./layer.a4);
Y = layer.a1*(X/100) + layer.a2*(X/100).^2 + layer.a3*(X/100).^3 + layer.a4*(X/100).^4;
end
end
end
The network is very simple — only the fitCurveLayer defines the learnable parameters a1–a4. I observed that the output values are very close to those from lsqcurvefit.
John Brown
John Brown
Letzte Aktivitätam 29 Aug. 2025

Function Syntax Design Conundrum
As a MATLAB enthusiast, I particularly enjoy Steve Eddins' blog and the cool things he explores. MATLAB's new argument blocks are great, but there's one frustrating limitation that Steve outlined beautifully in his blog post "Function Syntax Design Conundrum": cases where an argument should accept both enumerated values AND other data types.
Steve points out this could be done using the input parser, but I prefer having tab completions and I'm not a fan of maintaining function signature JSON files for all my functions.
Personal Context on Enumerations
To be clear: I honestly don't like enumerations in any way, shape, or form. One reason is how awkward they are. I've long suspected they're simply predefined constructor calls with a set argument, and I think that's all but confirmed here. This explains why I've had to fight the enumeration system when trying to take arguments of many types and normalize them to enumerated members, or have numeric values displayed as enumerated members without being recast to the superclass every operation.
The Discovery
While playing around extensively with metadata for another project, I realized (and I'm entirely unsure why it took so long) that the properties of a metaclass object are just, in many cases, the attributes of the classdef. In this realization, I found a solution to Steve's and my problem.
To be clear: I'm not in love with this solution. I would much prefer a better approach for allowing variable sets of membership validation for arguments. But as it stands, we don't have that, so here's an interesting, if incredibly hacky, solution.
If you call struct() on a metaclass object to view its hidden properties, you'll notice that in addition to the public "Enumeration" property, there's a hidden "Enumerable" property. They're both logicals, which implies they're likely functionally distinct. I was curious about that distinction and hoped to find some functionality by intentionally manipulating these values - and I did, solving the exact problem Steve mentions.
The Problem Statement
We have a function with an argument that should allow "dual" input types: enumerated values (Steve's example uses days of the week, mine uses the "all" option available in various dimension-operating functions) AND integers. We want tab completion for the enumerated values while still accepting the numeric inputs.
A Solution for Tab-Completion Supported Arguments
Rather than spoil Steve's blog post, let me use my own example: implementing a none() function. The definition is simple enough tf = ~any(A, dim); but when we wrap this in another function, we lose the tab-completion that any() provides for the dim argument (which gives you "all"). There's no great way to implement this as a function author currently - at least, that's well documented.
So here's my solution:
%% Example Function Implementation
% This is a simple implementation of the DimensionArgument class for implementing dual type inputs that allow enumerated tab-completion.
function tf = none(A, dim)
arguments(Input)
A logical;
dim DimensionArgument = DimensionArgument(A, true);
end
% Simple example (notice the use of uplus to unwrap the hidden property)
tf = ~any(A, +dim);
end
I like this approach because the additional work required to implement it, once the enumeration class is initialized, is minimal. Here are examples of function calls, note that the behavior parallels that of the MATLAB native-style tab-completion:
%% Test Data
% Simple logical array for testing
A = randi([0, 1], [3, 5], "logical");
%% Example function calls
tf = none(A, "all"); % This is the tab-completion it's 1:1 with MATLABs behavior
tf = none(A, [1, 2]); % We can still use valid arguments (validated in the constructor)
tf = none(A); % Showcase of the constructors use as a default argument generator
How It Works
What makes this work is the previously mentioned Enumeration attribute. By setting Enumeration = false while still declaring an enumeration block in the classdef file, we get the suggested members as auto-complete suggestions. As I hinted at, the value of enumerations (if you don't subclass a builtin and define values with the someMember (1) syntax) are simply arguments to constructor calls.
We also get full control over the storage and handling of the class, which means we lose the implicit storage that enumerations normally provide and are responsible for doing so ourselves - but I much prefer this. We can implement internal validation logic to ensure values that aren't in the enumerated set still comply with our constraints, and store the input (whether the enumerated member or alternative type) in an internal property.
As seen in the example class below, this maintains a convenient interface for both the function caller and author the only particuarly verbose portion is the conversion methods... Which if your willing to double down on the uplus unwrapping context can be avoided. What I have personally done is overload the uplus function to return the input (or perform the identity property) this allowss for the uplus to be used universally to unwrap inputs and for those that cant, and dont have a uplus definition, the value itself is just returned:
classdef(Enumeration = false) DimensionArgument % < matlab.mixin.internal.MatrixDisplay
%DimensionArgument Enumeration class to provide auto-complete on functions needing the dimension type seen in all()
% Enumerations are just macros to make constructor calls with a known set of arguments. Declaring the 'all'
% enumeration member means this class can be set as the type for an input and the auto-completion for the given
% argument will show the enumeration members, allowing tab-completion. Declaring the Enumeration attribute of
% the class as false gives us control over the constructor and internal implementation. As such we can use it
% to validate the numeric inputs, in the event the 'all' option was not used, and return an object that will
% then work in place of valid dimension argument options.
%% Enumeration members
% These are the auto-complete options you'd like to make available for the function signature for a given
% argument.
enumeration(Description="Enumerated value for the dimension argument.")
all
end
%% Properties
% The internal property allows the constructor's input to be stored; this ensures that the value is store and
% that the output of the constructor has the class type so that the validation passes.
% (Constructors must return the an object of the class they're a constructor for)
properties(Hidden, Description="Storage of the constructor input for later use.")
Data = [];
end
%% Constructor method
% By the magic of declaring (Enumeration = false) in our class def arguments we get full control over the
% constructor implementation.
%
% The second argument in this specific instance is to enable the argument's default value to be set in the
% arguments block itself as opposed to doing so in the function body... I like this better but if you didn't
% you could just as easily keep the constructor simple.
methods
function obj = DimensionArgument(A, Adim)
%DimensionArgument Initialize the dimension argument.
arguments
% This will be the enumeration member name from auto-completed entries, or the raw user input if not
% used.
A = [];
% A flag that indicates to create the value using different logic, in this case the first non-singleton
% dimension, because this matches the behavior of functions like, all(), sum() prod(), etc.
Adim (1, 1) logical = false;
end
if(Adim)
% Allows default initialization from an input to match the aforemention function's behavior
obj.Data = firstNonscalarDim(A);
else
% As a convenience for this style of implementation we can validate the input to ensure that since we're
% suppose to be an enumeration, the input is valid
DimensionArgument.mustBeValidMember(A);
% Store the input in a hidden property since declaring ~Enumeration means we are responsible for storing
% it.
obj.Data = A;
end
end
end
%% Conversion methods
% Applies conversion to the data property so that implicit casting of functions works. Unfortunately most of
% the MathWorks defined functions use a different system than that employed by the arguments block, which
% defers to the class defined converter methods... Which is why uplus (+obj) has been defined to unwrap the
% data for ease of use.
methods
function obj = uplus(obj)
obj = obj.Data;
end
function str = char(obj)
str = char(obj.Data);
end
function str = cellstr(obj)
str = cellstr(obj.Data);
end
function str = string(obj)
str = string(obj.Data);
end
function A = double(obj)
A = double(obj.Data);
end
function A = int8(obj)
A = int8(obj.Data);
end
function A = int16(obj)
A = int16(obj.Data);
end
function A = int32(obj)
A = int32(obj.Data);
end
function A = int64(obj)
A = int64(obj.Data);
end
end
%% Validation methods
% These utility methods are for input validation
methods(Static, Access = private)
function tf = isValidMember(obj)
%isValidMember Checks that the input is a valid dimension argument.
tf = (istext(obj) && all(obj == "all", "all")) || (isnumeric(obj) && all(isint(obj) & obj > 0, "all"));
end
function mustBeValidMember(obj)
%mustBeValidMember Validates that the input is a valid dimension argument for the dim/dimVec arguments.
if(~DimensionArgument.isValidMember(obj))
exception("JB:DimensionArgument:InvalidInput", "Input must be an integer value or the term 'all'.")
end
end
end
%% Convenient data display passthrough
methods
function disp(obj, name)
arguments
obj DimensionArgument
name string {mustBeScalarOrEmpty} = [];
end
% Dispatch internal data's display implementation
display(obj.Data, char(name));
end
end
end
In the event you'd actually play with theres here are the function definitions for some of the utility functions I used in them, including my exception would be a pain so i wont, these cases wont use it any...
% Far from my definition isint() but is consistent with mustBeInteger() for real numbers but will suffice for the example
function tf = isint(A)
arguments
A {mustBeNumeric(A)};
end
tf = floor(A) == A
end
% Sort of the same but its fine
function dim = firstNonscalarDim(A)
arguments
A
end
dim = [find(size(A) > 1, 1), 0];
dim(1) = dim(1);
end
Yann Debray
Yann Debray
Letzte Aktivitätam 26 Aug. 2025

Hello MATLAB Central, this is my first article.
My name is Yann. And I love MATLAB.
I also love HTTP (i know, weird fetish)
So i started a conversation with ChatGPT about it:
gitclone('https://github.com/yanndebray/HTTP-with-MATLAB');
cd('HTTP-with-MATLAB')
http_with_MATLAB
data = struct with fields:
data: [1×1 struct]
btcPrice = 1.0949e+05
age = struct with fields:
count: 27549 name: 'Yann' age: 51
Error using loadenv (line 27)
Unable to find or open '.env'. Check the path and filename or file permissions.

Error in http_with_MATLAB (line 18)
loadenv(".env")
^^^^^^^^^^^^^^^
I'm not sure that this platform is intended to clone repos from github, but i figured I'd paste this shortcut in case you want to try out my live script http_with_MATLAB.m
A lot of what i program lately relies on external web services (either for fetching data, or calling LLMs).
So I wrote a small tutorial of the 7 or so things I feel like I need to remember when making HTTP requests in MATLAB.
Let me know what you think
Summary:
Dynamically accessing variable names can negatively impact the readability of your code and can cause it to run slower by preventing MATLAB from optimizing it as well as it could if you used alternate techniques. The most common alternative is to use simple and efficient indexing.
Explanation:
Sometimes beginners (and some self-taught professors) think it would be a good idea to dynamically create or access variable names, the variables are often named something like these:
  • matrix1, matrix2, matrix3, matrix4, ...
  • test_20kmh, test_50kmh, test_80kmh, ...
  • nameA, nameB, nameC, nameD,...
Good reasons why dynamic variable names should be avoided:
There are much better alternatives to accessing dynamic variable names:
Note that avoiding eval (and assignin, etc.) is not some esoteric MATLAB restriction, it also applies to many other programming languages as well:
MATLAB Documentation:
If you are not interested in reading the answers below then at least read MATLAB's own documentation on this topic Alternatives to the eval Function, which states "A frequent use of the eval function is to create sets of variables such as A1, A2, ..., An, but this approach does not use the array processing power of MATLAB and is not recommended. The preferred method is to store related data in a single array." Data in a single array can be accessed very efficiently using indexing.
Note that all of these problems and disadvantages also apply to functions load (without an output variable), assignin, evalin, and evalc, and the MATLAB documentation explicitly recommends to "Avoid functions such as eval, evalc, evalin, and feval(fname)".
The official MATLAB blogs explain why eval should be avoided, the better alternatives to eval, and clearly recommend against magically creating variables. Using eval comes out at position number one on this list of Top 10 MATLAB Code Practices That Make Me Cry. Experienced MATLAB users recommend avoiding using eval for trivial code, and have written extensively on this topic.
Jan Studnicka
Jan Studnicka
Letzte Aktivitätam 22 Aug. 2025

Did you know that function double with string vector input significantly outperforms str2double with the same input:
x = rand(1,50000);
t = string(x);
tic; str2double(t); toc
Elapsed time is 0.276966 seconds.
tic; I1 = str2double(t); toc
Elapsed time is 0.244074 seconds.
tic; I2 = double(t); toc
Elapsed time is 0.002907 seconds.
isequal(I1,I2)
ans = logical
1
Recently I needed to parse numbers from text. I automatically tried to use str2double. However, profiling revealed that str2double was the main bottleneck in my code. Than I realized that there is a new note (since R2024a) in the documentation of str2double:
"Calling string and then double is recommended over str2double because it provides greater flexibility and allows vectorization. For additional information, see Alternative Functionality."
t = turtle(); % Start a turtle
t.forward(100); % Move forward by 100
t.backward(100); % Move backward by 100
t.left(90); % Turn left by 90 degrees
t.right(90); % Tur right by 90 degrees
t.goto(100, 100); % Move to (100, 100)
t.turnto(90); % Turn to 90 degrees, i.e. north
t.speed(1000); % Set turtle speed as 1000 (default: 500)
t.pen_up(); % Pen up. Turtle leaves no trace.
t.pen_down(); % Pen down. Turtle leaves a trace again.
t.color('b'); % Change line color to 'b'
t.begin_fill(FaceColor, EdgeColor, FaceAlpha); % Start filling
t.end_fill(); % End filling
t.change_icon('person.png'); % Change the icon to 'person.png'
t.clear(); % Clear the Axes
classdef turtle < handle
properties (GetAccess = public, SetAccess = private)
x = 0
y = 0
q = 0
end
properties (SetAccess = public)
speed (1, 1) double = 500
end
properties (GetAccess = private)
speed_reg = 100
n_steps = 20
ax
l
ht
im
is_pen_up = false
is_filling = false
fill_color
fill_alpha
end
methods
function obj = turtle()
figure(Name='MATurtle', NumberTitle='off')
obj.ax = axes(box="on");
hold on,
obj.ht = hgtransform();
icon = flipud(imread('turtle.png'));
obj.im = imagesc(obj.ht, icon, ...
XData=[-30, 30], YData=[-30, 30], ...
AlphaData=(255 - double(rgb2gray(icon)))/255);
obj.l = plot(obj.x, obj.y, 'k');
obj.ax.XLim = [-500, 500];
obj.ax.YLim = [-500, 500];
obj.ax.DataAspectRatio = [1, 1, 1];
obj.ax.Toolbar.Visible = 'off';
disableDefaultInteractivity(obj.ax);
end
function home(obj)
obj.x = 0;
obj.y = 0;
obj.ht.Matrix = eye(4);
end
function forward(obj, dist)
obj.step(dist);
end
function backward(obj, dist)
obj.step(-dist)
end
function step(obj, delta)
if numel(delta) == 1
delta = delta*[cosd(obj.q), sind(obj.q)];
end
if obj.is_filling
obj.fill(delta);
else
obj.move(delta);
end
end
function goto(obj, x, y)
dx = x - obj.x;
dy = y - obj.y;
obj.turnto(rad2deg(atan2(dy, dx)));
obj.step([dx, dy]);
end
function left(obj, q)
obj.turn(q);
end
function right(obj, q)
obj.turn(-q);
end
function turnto(obj, q)
obj.turn(obj.wrap_angle(q - obj.q, -180));
end
function pen_up(obj)
if obj.is_filling
warning('not available while filling')
return
end
obj.is_pen_up = true;
end
function pen_down(obj, go)
if obj.is_pen_up
if nargin == 1
obj.l(end+1) = plot(obj.x, obj.y, Color=obj.l(end).Color);
else
obj.l(end+1) = go;
end
uistack(obj.ht, 'top')
end
obj.is_pen_up = false;
end
function color(obj, line_color)
if obj.is_filling
warning('not available while filling')
return
end
obj.pen_up();
obj.pen_down(plot(obj.x, obj.y, Color=line_color));
end
function begin_fill(obj, FaceColor, EdgeColor, FaceAlpha)
arguments
obj
FaceColor = [.6, .9, .6];
EdgeColor = [0 0.4470 0.7410];
FaceAlpha = 1;
end
if obj.is_filling
warning('already filling')
return
end
obj.fill_color = FaceColor;
obj.fill_alpha = FaceAlpha;
obj.pen_up();
obj.pen_down(patch(obj.x, obj.y, [1, 1, 1], ...
EdgeColor=EdgeColor, FaceAlpha=0));
obj.is_filling = true;
end
function end_fill(obj)
if ~obj.is_filling
warning('not filling now')
return
end
obj.l(end).FaceColor = obj.fill_color;
obj.l(end).FaceAlpha = obj.fill_alpha;
obj.is_filling = false;
end
function change_icon(obj, filename)
icon = flipud(imread(filename));
obj.im.CData = icon;
obj.im.AlphaData = (255 - double(rgb2gray(icon)))/255;
end
function clear(obj)
obj.x = 0;
obj.y = 0;
delete(obj.ax.Children(2:end));
obj.l = plot(0, 0, 'k');
obj.ht.Matrix = eye(4);
end
end
methods (Access = private)
function animated_step(obj, delta, q, initFcn, updateFcn)
arguments
obj
delta
q
initFcn = @() []
updateFcn = @(~, ~) []
end
dx = delta(1)/obj.n_steps;
dy = delta(2)/obj.n_steps;
dq = q/obj.n_steps;
pause_duration = norm(delta)/obj.speed/obj.speed_reg;
initFcn();
for i = 1:obj.n_steps
updateFcn(dx, dy);
obj.ht.Matrix = makehgtform(...
translate=[obj.x + dx*i, obj.y + dy*i, 0], ...
zrotate=deg2rad(obj.q + dq*i));
pause(pause_duration)
drawnow limitrate
end
obj.x = obj.x + delta(1);
obj.y = obj.y + delta(2);
end
function obj = turn(obj, q)
obj.animated_step([0, 0], q);
obj.q = obj.wrap_angle(obj.q + q, 0);
end
function move(obj, delta)
initFcn = @() [];
updateFcn = @(dx, dy) [];
if ~obj.is_pen_up
initFcn = @() initializeLine();
updateFcn = @(dx, dy) obj.update_end_point(obj.l(end), dx, dy);
end
function initializeLine()
obj.l(end).XData(end+1) = obj.l(end).XData(end);
obj.l(end).YData(end+1) = obj.l(end).YData(end);
end
obj.animated_step(delta, 0, initFcn, updateFcn);
end
function obj = fill(obj, delta)
initFcn = @() initializePatch();
updateFcn = @(dx, dy) obj.update_end_point(obj.l(end), dx, dy);
function initializePatch()
obj.l(end).Vertices(end+1, :) = obj.l(end).Vertices(end, :);
obj.l(end).Faces = 1:size(obj.l(end).Vertices, 1);
end
obj.animated_step(delta, 0, initFcn, updateFcn);
end
end
methods (Static, Access = private)
function update_end_point(l, dx, dy)
l.XData(end) = l.XData(end) + dx;
l.YData(end) = l.YData(end) + dy;
end
function q = wrap_angle(q, min_angle)
q = mod(q - min_angle, 360) + min_angle;
end
end
end
I am thrilled python interoperability now seems to work for me with my APPLE M1 MacBookPro and MATLAB V2025a. The available instructions are still, shall we say, cryptic. Here is a summary of my interaction with GPT 4o to get this to work.
===========================================================
MATLAB R2025a + Python (Astropy) Integration on Apple Silicon (M1/M2/M3 Macs)
===========================================================
Author: D. Carlsmith, documented with ChatGPT
Last updated: July 2025
This guide provides full instructions, gotchas, and workarounds to run Python 3.10 with MATLAB R2025a (Apple Silicon/macOS) using native ARM64 Python and calling modules like Astropy, Numpy, etc. from within MATLAB.
===========================================================
Overview
===========================================================
- MATLAB R2025a on Apple Silicon (M1/M2/M3) runs as "maca64" (native ARM64).
- To call Python from MATLAB, the Python interpreter must match that architecture (ARM64).
- Using Intel Python (x86_64) with native MATLAB WILL NOT WORK.
- The cleanest solution: use Miniforge3 (Conda-forge's lightweight ARM64 distribution).
===========================================================
1. Install Miniforge3 (ARM64-native Conda)
===========================================================
In Terminal, run:
curl -LO https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-MacOSX-arm64.sh
bash Miniforge3-MacOSX-arm64.sh
Follow prompts:
- Press ENTER to scroll through license.
- Type "yes" when asked to accept the license.
- Press ENTER to accept the default install location: ~/miniforge3
- When asked:
Do you wish to update your shell profile to automatically initialize conda? [yes|no]
Type: yes
===========================================================
2. Restart Terminal and Create a Python Environment for MATLAB
===========================================================
Run the following:
conda create -n matlab python=3.10 astropy numpy -y
conda activate matlab
Verify the Python path:
which python
Expected output:
/Users/YOURNAME/miniforge3/envs/matlab/bin/python
===========================================================
3. Verify Python + Astropy From Terminal
===========================================================
Run:
python -c "import astropy; print(astropy.__version__)"
Expected output:
6.x.x (or similar)
===========================================================
4. Configure MATLAB to Use This Python
===========================================================
In MATLAB R2025a (Apple Silicon):
clear classes
pyenv('Version', '/Users/YOURNAME/miniforge3/envs/matlab/bin/python')
py.sys.version
You should see the Python version printed (e.g. 3.10.18). No error means it's working.
===========================================================
5. Gotchas and Their Solutions
===========================================================
❌ Error: Python API functions are not available
→ Cause: Wrong architecture or broken .dylib
→ Fix: Use Miniforge ARM64 Python. DO NOT use Intel Anaconda.
❌ Error: Invalid text character (↑ points at __version__)
→ Cause: MATLAB can’t parse double underscores typed or pasted
→ Fix: Use: py.getattr(module, '__version__')
❌ Error: Unrecognized method 'separation' or 'sec'
→ Cause: MATLAB can't reflect dynamic Python methods
→ Fix: Use: py.getattr(obj, 'method')(args)
===========================================================
6. Run Full Verification in MATLAB
===========================================================
Paste this into MATLAB:
% Set environment
clear classes
pyenv('Version', '/Users/YOURNAME/miniforge3/envs/matlab/bin/python');
% Import modules
coords = py.importlib.import_module('astropy.coordinates');
time_mod = py.importlib.import_module('astropy.time');
table_mod = py.importlib.import_module('astropy.table');
% Astropy version
ver = char(py.getattr(py.importlib.import_module('astropy'), '__version__'));
disp(['Astropy version: ', ver]);
% SkyCoord angular separation
c1 = coords.SkyCoord('10h21m00s', '+41d12m00s', pyargs('frame', 'icrs'));
c2 = coords.SkyCoord('10h22m00s', '+41d15m00s', pyargs('frame', 'icrs'));
sep_fn = py.getattr(c1, 'separation');
sep = sep_fn(c2);
arcsec = double(sep.to('arcsec').value);
fprintf('Angular separation = %.3f arcsec\n', arcsec);
% Time difference in seconds
Time = time_mod.Time;
t1 = Time('2025-01-01T00:00:00', pyargs('format','isot','scale','utc'));
t2 = Time('2025-01-02T00:00:00', pyargs('format','isot','scale','utc'));
dt = py.getattr(t2, '__sub__')(t1);
seconds = double(py.getattr(dt, 'sec'));
fprintf('Time difference = %.0f seconds\n', seconds);
% Astropy table display
tbl = table_mod.Table(pyargs('names', {'a','b'}, 'dtype', {'int','float'}));
tbl.add_row({1, 2.5});
tbl.add_row({2, 3.7});
disp(tbl);
===========================================================
7. Optional: Automatically Configure Python in startup.m
===========================================================
To avoid calling pyenv() every time, edit your MATLAB startup:
edit startup.m
Add:
try
pyenv('Version', '/Users/YOURNAME/miniforge3/envs/matlab/bin/python');
catch
warning("Python already loaded.");
end
===========================================================
8. Final Notes
===========================================================
- This setup avoids all architecture mismatches.
- It uses a clean, minimal ARM64 Python that integrates seamlessly with MATLAB.
- Do not mix Anaconda (Intel) with Apple Silicon MATLAB.
- Use py.getattr for any Python attribute containing underscores or that MATLAB can't resolve.
You can now run NumPy, Astropy, Pandas, Astroquery, Matplotlib, and more directly from MATLAB.
===========================================================
I would like to zoom directly on the selected region when using on my image created with image or imagesc. First of all, I would recommend using image or imagesc and not imshow for this case, see comparison here: Differences between imshow() and image()? However when zooming Stretch-to-Fill behavior happens and I don't want that. Try range zoom to image generated by this code:
fig = uifigure;
ax = uiaxes(fig);
im = imread("peppers.png");
h = imagesc(im,"Parent",ax);
axis(ax,'tight', 'off')
I can fix that with manualy setting data aspect ratio:
daspect(ax,[1 1 1])
However, I need this code to run automatically after zooming. So I create zoom object and ActionPostCallback which is called everytime after I zoom, see zoom - ActionPostCallback.
z = zoom(ax);
z.ActionPostCallback = @(fig,ax) daspect(ax.Axes,[1 1 1]);
If you need, you can also create ActionPreCallback which is called everytime before I zoom, see zoom - ActionPreCallback.
z.ActionPreCallback = @(fig,ax) daspect(ax.Axes,'auto');
Code written and run in R2025a.
The beautiful and elegant chord diagrams were all created using MATLAB?
Indeed, they were all generated using the chord diagram plotting toolkit that I developed myself:
You can download these toolkits from the provided links.
The reason for writing this article is that many people have started using the chord diagram plotting toolkit that I developed. However, some users are unsure about customizing certain styles. As the developer, I have a good understanding of the implementation principles of the toolkit and can apply it flexibly. This has sparked the idea of challenging myself to create various styles of chord diagrams. Currently, the existing code is quite lengthy. In the future, I may integrate some of this code into the toolkit, enabling users to achieve the effects of many lines of code with just a few lines.
Without further ado, let's see the extent to which this MATLAB toolkit can currently perform.
demo 1
rng(2)
dataMat = randi([0,5], [11,5]);
dataMat(1:6,1) = 0;
dataMat([11,7],1) = [45,25];
dataMat([1,4,5,7],2) = [20,20,30,30];
dataMat(:,3) = 0;
dataMat(6,3) = 45;
dataMat(1:5,4) = 0;
dataMat([6,7],4) = [25,25];
dataMat([5,6,9],5) = [25,25,25];
colName = {'Fly', 'Beetle', 'Leaf', 'Soil', 'Waxberry'};
rowName = {'Bartomella', 'Bradyrhizobium', 'Dysgomonas', 'Enterococcus',...
'Lactococcus', 'norank', 'others', 'Pseudomonas', 'uncultured',...
'Vibrionimonas', 'Wolbachia'};
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
CC = chordChart(dataMat, 'rowName',rowName, 'colName',colName, 'Sep',1/80);
CC = CC.draw();
% 修改上方方块颜色(Modify the color of the blocks above)
CListT = [0.7765 0.8118 0.5216; 0.4431 0.4706 0.3843; 0.5804 0.2275 0.4549;
0.4471 0.4039 0.6745; 0.0157 0 0 ];
for i = 1:size(dataMat, 2)
CC.setSquareT_N(i, 'FaceColor',CListT(i,:))
end
% 修改下方方块颜色(Modify the color of the blocks below)
CListF = [0.5843 0.6863 0.7843; 0.1098 0.1647 0.3255; 0.0902 0.1608 0.5373;
0.6314 0.7961 0.2118; 0.0392 0.2078 0.1059; 0.0157 0 0 ;
0.8549 0.9294 0.8745; 0.3882 0.3255 0.4078; 0.5020 0.7216 0.3843;
0.0902 0.1843 0.1804; 0.8196 0.2314 0.0706];
for i = 1:size(dataMat, 1)
CC.setSquareF_N(i, 'FaceColor',CListF(i,:))
end
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
CC.setChordMN(i,j, 'FaceColor',CListT(j,:), 'FaceAlpha',.5)
end
end
CC.tickState('on')
CC.labelRotate('on')
CC.setFont('FontSize',17, 'FontName','Cambria')
% CC.labelRotate('off')
% textHdl = findobj(gca,'Tag','ChordLabel');
% for i = 1:length(textHdl)
% if textHdl(i).Position(2) < 0
% if abs(textHdl(i).Position(1)) > .7
% textHdl(i).Rotation = textHdl(i).Rotation + 45;
% textHdl(i).HorizontalAlignment = 'right';
% if textHdl(i).Rotation > 90
% textHdl(i).Rotation = textHdl(i).Rotation + 180;
% textHdl(i).HorizontalAlignment = 'left';
% end
% else
% textHdl(i).Rotation = textHdl(i).Rotation + 10;
% textHdl(i).HorizontalAlignment = 'right';
% end
% end
% end
demo 2
rng(3)
dataMat = randi([1,15], [7,22]);
dataMat(dataMat < 11) = 0;
dataMat(1, sum(dataMat, 1) == 0) = 15;
colName = {'A2M', 'FGA', 'FGB', 'FGG', 'F11', 'KLKB1', 'SERPINE1', 'VWF',...
'THBD', 'TFPI', 'PLAT', 'SERPINA5', 'SERPIND1', 'F2', 'PLG', 'F12',...
'SERPINC1', 'SERPINA1', 'PROS1', 'SERPINF2', 'F13A1', 'PROC'};
rowName = {'Lung', 'Spleen', 'Liver', 'Heart',...
'Renal cortex', 'Renal medulla', 'Thyroid'};
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
CC = chordChart(dataMat, 'rowName',rowName, 'colName',colName, 'Sep',1/80, 'LRadius',1.21);
CC = CC.draw();
CC.labelRotate('on')
% 单独设置每一个弦末端方块(Set individual end blocks for each chord)
% Use obj.setEachSquareF_Prop
% or obj.setEachSquareT_Prop
% F means from (blocks below)
% T means to (blocks above)
CListT = [173,70,65; 79,135,136]./255;
% Upregulated:1 | Downregulated:2
Regulated = rand([7, 22]);
Regulated = (Regulated < .8) + 1;
for i = 1:size(Regulated, 1)
for j = 1:size(Regulated, 2)
CC.setEachSquareT_Prop(i, j, 'FaceColor', CListT(Regulated(i,j),:))
end
end
% 绘制图例(Draw legend)
H1 = fill([0,1,0] + 100, [1,0,1] + 100, CListT(1,:), 'EdgeColor','none');
H2 = fill([0,1,0] + 100, [1,0,1] + 100, CListT(2,:), 'EdgeColor','none');
lgdHdl = legend([H1,H2], {'Upregulated','Downregulated'}, 'AutoUpdate','off', 'Location','best');
lgdHdl.ItemTokenSize = [12,12];
lgdHdl.Box = 'off';
lgdHdl.FontSize = 13;
% 修改下方方块颜色(Modify the color of the blocks below)
CListF = [128,108,171; 222,208,161; 180,196,229; 209,150,146; 175,201,166;
134,156,118; 175,175,173]./255;
for i = 1:size(dataMat, 1)
CC.setSquareF_N(i, 'FaceColor',CListF(i,:))
end
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
CC.setChordMN(i,j, 'FaceColor',CListF(i,:), 'FaceAlpha',.45)
end
end
demo 3
dataMat = rand([15,15]);
dataMat(dataMat > .15) = 0;
CList = [ 75,146,241; 252,180, 65; 224, 64, 10; 5,100,146; 191,191,191;
26, 59,105; 255,227,130; 18,156,221; 202,107, 75; 0, 92,219;
243,210,136; 80, 99,129; 241,185,168; 224,131, 10; 120,147,190]./255;
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList);
BCC = BCC.draw();
% 添加刻度
BCC.tickState('on')
% 修改字体,字号及颜色
BCC.setFont('FontName','Cambria', 'FontSize',17, 'Color',[0,0,.8])
demo 4
rng(5)
dataMat = randi([1,20], [5,5]);
dataMat(1,1) = 110;
dataMat(2,2) = 40;
dataMat(3,3) = 50;
dataMat(5,5) = 50;
CList1 = [164,190,158; 216,213,153; 177,192,208; 238,238,227; 249,217,153]./255;
CList2 = [247,204,138; 128,187,185; 245,135,124; 140,199,197; 252,223,164]./255;
CList = CList2;
NameList={'CHORD','CHART','MADE','BY','SLANDARER'};
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList, 'Sep',1/30, 'Label',NameList, 'LRadius',1.33);
BCC = BCC.draw();
% 添加刻度
BCC.tickState('on')
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
if dataMat(i,j) > 0
BCC.setChordMN(i,j, 'FaceAlpha',.7, 'EdgeColor',CList(i,:)./1.1)
end
end
end
% 修改方块颜色(Modify the color of the blocks)
for i = 1:size(dataMat, 1)
BCC.setSquareN(i, 'EdgeColor',CList(i,:)./1.7)
end
% 修改字体,字号及颜色
BCC.setFont('FontName','Cambria', 'FontSize',17)
BCC.tickLabelState('on')
BCC.setTickFont('FontName','Cambria', 'FontSize',9)
demo 5
dataMat=randi([1,20], [14,3]);
dataMat(11:14,1) = 0;
dataMat(6:10,2) = 0;
dataMat(1:5,3) = 0;
colName = compose('C%d', 1:3);
rowName = [compose('A%d', 1:7), compose('B%d', 7:-1:1)];
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
CC = chordChart(dataMat, 'rowName',rowName, 'colName',colName, 'Sep',1/80);
CC = CC.draw();
% 修改上方方块颜色(Modify the color of the blocks above)
for i = 1:size(dataMat, 2)
CC.setSquareT_N(i, 'FaceColor',[190,190,190]./255)
end
% 修改下方方块颜色(Modify the color of the blocks below)
CListF=[255,244,138; 253,220,117; 254,179, 78; 253,190, 61;
252, 78, 41; 228, 26, 26; 178, 0, 36; 4, 84,119;
1,113,137; 21,150,155; 67,176,173; 68,173,158;
123,204,163; 184,229,162]./255;
for i = 1:size(dataMat, 1)
CC.setSquareF_N(i, 'FaceColor',CListF(i,:))
end
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
CC.setChordMN(i,j, 'FaceColor',CListF(i,:), 'FaceAlpha',.5)
end
end
CC.tickState('on')
CC.tickLabelState('on')
demo 6
rng(2)
dataMat = randi([0,40], [20,4]);
dataMat(rand([20,4]) < .2) = 0;
dataMat(1,3) = 500;
dataMat(20,1:4) = [140; 150; 80; 90];
colName = compose('T%d', 1:4);
rowName = compose('SL%d', 1:20);
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
CC = chordChart(dataMat, 'rowName',rowName, 'colName',colName, 'Sep',1/80, 'LRadius',1.23);
CC = CC.draw();
% 修改上方方块颜色(Modify the color of the blocks above)
CListT = [0.62,0.49,0.27; 0.28,0.57,0.76
0.25,0.53,0.30; 0.86,0.48,0.34];
for i = 1:size(dataMat, 2)
CC.setSquareT_N(i, 'FaceColor',CListT(i,:))
end
% 修改下方方块颜色(Modify the color of the blocks below)
CListF = [0.94,0.84,0.60; 0.16,0.50,0.67; 0.92,0.62,0.49;
0.48,0.44,0.60; 0.48,0.44,0.60; 0.71,0.79,0.73;
0.96,0.98,0.98; 0.51,0.82,0.95; 0.98,0.70,0.82;
0.97,0.85,0.84; 0.55,0.64,0.62; 0.94,0.93,0.60;
0.98,0.90,0.85; 0.72,0.84,0.81; 0.85,0.45,0.49;
0.76,0.76,0.84; 0.59,0.64,0.62; 0.62,0.14,0.15;
0.75,0.75,0.75; 1.00,1.00,1.00];
for i = 1:size(dataMat, 1)
CC.setSquareF_N(i, 'FaceColor',CListF(i,:))
end
CC.setSquareF_N(size(dataMat, 1), 'EdgeColor','k', 'LineWidth',1)
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
CC.setChordMN(i,j, 'FaceColor',CListT(j,:), 'FaceAlpha',.46)
end
end
CC.tickState('on')
CC.labelRotate('on')
CC.setFont('FontSize',17, 'FontName','Cambria')
demo 7
dataMat = randi([10,10000], [10,10]);
dataMat(6:10,:) = 0;
dataMat(:,1:5) = 0;
NameList = {'BOC', 'ICBC', 'ABC', 'BOCM', 'CCB', ...
'yama', 'nikoto', 'saki', 'koto', 'kawa'};
CList = [0.63,0.75,0.88
0.67,0.84,0.75
0.85,0.78,0.88
1.00,0.92,0.93
0.92,0.63,0.64
0.57,0.67,0.75
1.00,0.65,0.44
0.72,0.73,0.40
0.65,0.57,0.58
0.92,0.94,0.96];
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList, 'Label',NameList);
BCC = BCC.draw();
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
if dataMat(i,j) > 0
BCC.setChordMN(i,j, 'FaceAlpha',.85, 'EdgeColor',CList(i,:)./1.5, 'LineWidth',.8)
end
end
end
for i = 1:size(dataMat, 1)
BCC.setSquareN(i, 'EdgeColor',CList(i,:)./1.5, 'LineWidth',1)
end
% 添加刻度、修改字体
BCC.tickState('on')
BCC.setFont('FontName','Cambria', 'FontSize',17)
demo 8
dataMat = rand([11,4]);
dataMat = round(10.*dataMat.*((11:-1:1).'+1))./10;
colName = {'A','B','C','D'};
rowName = {'Acidobacteriota', 'Actinobacteriota', 'Proteobacteria', ...
'Chloroflexi', 'Bacteroidota', 'Firmicutes', 'Gemmatimonadota', ...
'Verrucomicrobiota', 'Patescibacteria', 'Planctomyetota', 'Others'};
figure('Units','normalized', 'Position',[.02,.05,.8,.85])
CC = chordChart(dataMat, 'colName',colName, 'Sep',1/80, 'SSqRatio',30/100);% -30/100
CC = CC.draw();
% 修改上方方块颜色(Modify the color of the blocks above)
CListT = [0.93,0.60,0.62
0.55,0.80,0.99
0.95,0.82,0.18
1.00,0.81,0.91];
for i = 1:size(dataMat, 2)
CC.setSquareT_N(i, 'FaceColor',CListT(i,:))
end
% 修改下方方块颜色(Modify the color of the blocks below)
CListF = [0.75,0.73,0.86
0.56,0.83,0.78
0.00,0.60,0.20
1.00,0.49,0.02
0.78,0.77,0.95
0.59,0.24,0.36
0.98,0.51,0.45
0.96,0.55,0.75
0.47,0.71,0.84
0.65,0.35,0.16
0.40,0.00,0.64];
for i = 1:size(dataMat, 1)
CC.setSquareF_N(i, 'FaceColor',CListF(i,:))
end
% 修改弦颜色(Modify chord color)
CListC = [0.55,0.83,0.76
0.75,0.73,0.86
0.00,0.60,0.19
1.00,0.51,0.04];
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
CC.setChordMN(i,j, 'FaceColor',CListC(j,:), 'FaceAlpha',.4)
end
end
% 单独设置每一个弦末端方块(Set individual end blocks for each chord)
% Use obj.setEachSquareF_Prop
% or obj.setEachSquareT_Prop
% F means from (blocks below)
% T means to (blocks above)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
CC.setEachSquareT_Prop(i,j, 'FaceColor', CListF(i,:))
end
end
% 添加刻度
CC.tickState('on')
% 修改字体,字号及颜色
CC.setFont('FontName','Cambria', 'FontSize',17)
% 隐藏下方标签
textHdl = findobj(gca, 'Tag','ChordLabel');
for i = 1:length(textHdl)
if textHdl(i).Position(2) < 0
set(textHdl(i), 'Visible','off')
end
end
% 绘制图例(Draw legend)
scatterHdl = scatter(10.*ones(size(dataMat,1)),10.*ones(size(dataMat,1)), ...
55, 'filled');
for i = 1:length(scatterHdl)
scatterHdl(i).CData = CListF(i,:);
end
lgdHdl = legend(scatterHdl, rowName, 'Location','best', 'FontSize',16, 'FontName','Cambria', 'Box','off');
set(lgdHdl, 'Position',[.7482,.3577,.1658,.3254])
demo 9
dataMat = randi([0,10], [5,5]);
CList1 = [0.70,0.59,0.67
0.62,0.70,0.62
0.81,0.75,0.62
0.80,0.62,0.56
0.62,0.65,0.65];
CList2 = [0.02,0.02,0.02
0.59,0.26,0.33
0.38,0.49,0.38
0.03,0.05,0.03
0.29,0.28,0.32];
CList = CList2;
NameList={'CHORD','CHART','MADE','BY','SLANDARER'};
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList, 'Sep',1/30, 'Label',NameList, 'LRadius',1.33);
BCC = BCC.draw();
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
BCC.setChordMN(i,j, 'FaceAlpha',.5)
end
end
% 修改方块颜色(Modify the color of the blocks)
for i = 1:size(dataMat, 1)
BCC.setSquareN(i, 'EdgeColor',[0,0,0], 'LineWidth',5)
end
% 添加刻度
BCC.tickState('on')
% 修改字体,字号及颜色
BCC.setFont('FontSize',17, 'FontWeight','bold')
BCC.tickLabelState('on')
BCC.setTickFont('FontSize',9)
demo 10
rng(2)
dataMat = rand([14,5]) > .3;
colName = {'phosphorylation', 'vasculature development', 'blood vessel development', ...
'cell adhesion', 'plasma membrane'};
rowName = {'THY1', 'FGF2', 'MAP2K1', 'CDH2', 'HBEGF', 'CXCR4', 'ECSCR',...
'ACVRL1', 'RECK', 'PNPLA6', 'CDH5', 'AMOT', 'EFNB2', 'CAV1'};
figure('Units','normalized', 'Position',[.02,.05,.9,.85])
CC = chordChart(dataMat, 'colName',colName, 'rowName',rowName, 'Sep',1/80, 'LRadius',1.2);
CC = CC.draw();
% 修改上方方块颜色(Modify the color of the blocks above)
CListT1 = [0.5686 0.1961 0.2275
0.2275 0.2863 0.3765
0.8431 0.7882 0.4118
0.4275 0.4510 0.2706
0.3333 0.2706 0.2510];
CListT2 = [0.4941 0.5490 0.4118
0.9059 0.6510 0.3333
0.8980 0.6157 0.4980
0.8902 0.5137 0.4667
0.4275 0.2824 0.2784];
CListT3 = [0.4745 0.5843 0.7569
0.4824 0.5490 0.5843
0.6549 0.7216 0.6510
0.9412 0.9216 0.9059
0.9804 0.7608 0.6863];
CListT = CListT3;
for i = 1:size(dataMat, 2)
CC.setSquareT_N(i, 'FaceColor',CListT(i,:), 'EdgeColor',[0,0,0])
end
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
CC.setChordMN(i,j, 'FaceColor',CListT(j,:), 'FaceAlpha',.9, 'EdgeColor',[0,0,0])
end
end
% 修改下方方块颜色(Modify the color of the blocks below)
logFC = sort(rand(1,14))*6 - 3;
for i = 1:size(dataMat, 1)
CC.setSquareF_N(i, 'CData',logFC(i), 'FaceColor','flat', 'EdgeColor',[0,0,0])
end
CMap = [ 0 0 1.0000; 0.0645 0.0645 1.0000; 0.1290 0.1290 1.0000; 0.1935 0.1935 1.0000
0.2581 0.2581 1.0000; 0.3226 0.3226 1.0000; 0.3871 0.3871 1.0000; 0.4516 0.4516 1.0000
0.5161 0.5161 1.0000; 0.5806 0.5806 1.0000; 0.6452 0.6452 1.0000; 0.7097 0.7097 1.0000
0.7742 0.7742 1.0000; 0.8387 0.8387 1.0000; 0.9032 0.9032 1.0000; 0.9677 0.9677 1.0000
1.0000 0.9677 0.9677; 1.0000 0.9032 0.9032; 1.0000 0.8387 0.8387; 1.0000 0.7742 0.7742
1.0000 0.7097 0.7097; 1.0000 0.6452 0.6452; 1.0000 0.5806 0.5806; 1.0000 0.5161 0.5161
1.0000 0.4516 0.4516; 1.0000 0.3871 0.3871; 1.0000 0.3226 0.3226; 1.0000 0.2581 0.2581
1.0000 0.1935 0.1935; 1.0000 0.1290 0.1290; 1.0000 0.0645 0.0645; 1.0000 0 0];
colormap(CMap);
try clim([-3,3]),catch,end
try caxis([-3,3]),catch,end
CBHdl = colorbar();
CBHdl.Position = [0.74,0.25,0.02,0.2];
% =========================================================================
% 交换XY轴(Swap XY axis)
patchHdl = findobj(gca, 'Type','patch');
for i = 1:length(patchHdl)
tX = patchHdl(i).XData;
tY = patchHdl(i).YData;
patchHdl(i).XData = tY;
patchHdl(i).YData = - tX;
end
txtHdl = findobj(gca, 'Type','text');
for i = 1:length(txtHdl)
txtHdl(i).Position([1,2]) = [1,-1].*txtHdl(i).Position([2,1]);
if txtHdl(i).Position(1) < 0
txtHdl(i).HorizontalAlignment = 'right';
else
txtHdl(i).HorizontalAlignment = 'left';
end
end
lineHdl = findobj(gca, 'Type','line');
for i = 1:length(lineHdl)
tX = lineHdl(i).XData;
tY = lineHdl(i).YData;
lineHdl(i).XData = tY;
lineHdl(i).YData = - tX;
end
% =========================================================================
txtHdl = findobj(gca, 'Type','text');
for i = 1:length(txtHdl)
if txtHdl(i).Position(1) > 0
txtHdl(i).Visible = 'off';
end
end
text(1.25,-.15, 'LogFC', 'FontSize',16)
text(1.25,1, 'Terms', 'FontSize',16)
patchHdl = [];
for i = 1:size(dataMat, 2)
patchHdl(i) = fill([10,11,12],[10,13,13], CListT(i,:), 'EdgeColor',[0,0,0]);
end
lgdHdl = legend(patchHdl, colName, 'Location','best', 'FontSize',14, 'FontName','Cambria', 'Box','off');
lgdHdl.Position = [.735,.53,.167,.27];
lgdHdl.ItemTokenSize = [18,8];
demo 11
rng(2)
dataMat = rand([12,12]);
dataMat(dataMat < .85) = 0;
dataMat(7,:) = 1.*(rand(1,12)+.1);
dataMat(11,:) = .6.*(rand(1,12)+.1);
dataMat(12,:) = [2.*(rand(1,10)+.1), 0, 0];
CList = [repmat([49,49,49],[10,1]); 235,28,34; 19,146,241]./255;
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
BCC = biChordChart(dataMat, 'Arrow','off', 'CData',CList);
BCC = BCC.draw();
% 添加刻度
BCC.tickState('on')
% 修改字体,字号及颜色
BCC.setFont('FontName','Cambria', 'FontSize',17)
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
if dataMat(i,j) > 0
BCC.setChordMN(i,j, 'FaceAlpha',.78, 'EdgeColor',[0,0,0])
end
end
end
% 修改方块颜色(Modify the color of the blocks)
for i = 1:size(dataMat, 1)
BCC.setSquareN(i, 'EdgeColor',[0,0,0], 'LineWidth',2)
end
demo 12
dataMat = rand([9,9]);
dataMat(dataMat > .7) = 0;
dataMat(eye(9) == 1) = (rand([1,9])+.2).*3;
CList = [0.85,0.23,0.24
0.96,0.39,0.18
0.98,0.63,0.22
0.99,0.80,0.26
0.70,0.76,0.21
0.24,0.74,0.71
0.27,0.65,0.84
0.09,0.37,0.80
0.64,0.40,0.84];
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList);
BCC = BCC.draw();
% 添加刻度、刻度标签
BCC.tickState('on')
% 修改字体,字号及颜色
BCC.setFont('FontName','Cambria', 'FontSize',17)
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
if dataMat(i,j) > 0
BCC.setChordMN(i,j, 'FaceAlpha',.7)
end
end
end
demo 13
rng(2)
dataMat = randi([1,40], [7,4]);
dataMat(rand([7,4]) < .1) = 0;
colName = compose('MATLAB%d', 1:4);
rowName = compose('SL%d', 1:7);
figure('Units','normalized', 'Position',[.02,.05,.7,.85])
CC = chordChart(dataMat, 'rowName',rowName, 'colName',colName, 'Sep',1/80, 'LRadius',1.32);
CC = CC.draw();
% 修改上方方块颜色(Modify the color of the blocks above)
CListT = [0.49,0.64,0.53
0.75,0.39,0.35
0.80,0.74,0.42
0.40,0.55,0.66];
for i = 1:size(dataMat, 2)
CC.setSquareT_N(i, 'FaceColor',CListT(i,:))
end
% 修改下方方块颜色(Modify the color of the blocks below)
CListF = [0.91,0.91,0.97
0.62,0.95,0.66
0.91,0.61,0.20
0.54,0.45,0.82
0.99,0.76,0.81
0.91,0.85,0.83
0.53,0.42,0.43];
for i = 1:size(dataMat, 1)
CC.setSquareF_N(i, 'FaceColor',CListF(i,:))
end
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
CC.setChordMN(i,j, 'FaceColor',CListT(j,:), 'FaceAlpha',.46)
end
end
CC.tickState('on')
CC.tickLabelState('on')
CC.setFont('FontSize',17, 'FontName','Cambria')
CC.setTickFont('FontSize',8, 'FontName','Cambria')
% 绘制图例(Draw legend)
scatterHdl = scatter(10.*ones(size(dataMat,1)),10.*ones(size(dataMat,1)), ...
55, 'filled');
for i = 1:length(scatterHdl)
scatterHdl(i).CData = CListF(i,:);
end
lgdHdl = legend(scatterHdl, rowName, 'Location','best', 'FontSize',16, 'FontName','Cambria', 'Box','off');
set(lgdHdl, 'Position',[.77,.38,.1658,.27])
demo 14
rng(6)
dataMat = randi([1,20], [8,8]);
dataMat(dataMat > 5) = 0;
dataMat(1,:) = randi([1,15], [1,8]);
dataMat(1,8) = 40;
dataMat(8,8) = 60;
dataMat = dataMat./sum(sum(dataMat));
CList = [0.33,0.53,0.86
0.94,0.50,0.42
0.92,0.58,0.30
0.59,0.47,0.45
0.37,0.76,0.82
0.82,0.68,0.29
0.75,0.62,0.87
0.43,0.69,0.57];
NameList={'CHORD', 'CHART', 'AND', 'BICHORD',...
'CHART', 'MADE', 'BY', 'SLANDARER'};
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList, 'Sep',1/12, 'Label',NameList, 'LRadius',1.33);
BCC = BCC.draw();
% 添加刻度
BCC.tickState('on')
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
if dataMat(i,j) > 0
BCC.setChordMN(i,j, 'FaceAlpha',.7, 'EdgeColor',CList(i,:)./1.1)
end
end
end
% 修改方块颜色(Modify the color of the blocks)
for i = 1:size(dataMat, 1)
BCC.setSquareN(i, 'EdgeColor',CList(i,:)./1.7)
end
% 修改字体,字号及颜色
BCC.setFont('FontName','Cambria', 'FontSize',17)
BCC.tickLabelState('on')
BCC.setTickFont('FontName','Cambria', 'FontSize',9)
% 调整数值字符串格式
% Adjust numeric string format
BCC.setTickLabelFormat(@(x)[num2str(round(x*100)),'%'])
demo 15
CList = [0.81,0.72,0.83
0.69,0.82,0.89
0.17,0.44,0.64
0.70,0.85,0.55
0.03,0.57,0.13
0.97,0.67,0.64
0.84,0.09,0.12
1.00,0.80,0.46
0.98,0.52,0.01
];
figure('Units','normalized', 'Position',[.02,.05,.53,.85], 'Color',[1,1,1])
% =========================================================================
ax1 = axes('Parent',gcf, 'Position',[0,1/2,1/2,1/2]);
dataMat = rand([9,9]);
dataMat(dataMat > .4) = 0;
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList);
BCC = BCC.draw();
BCC.tickState('on')
BCC.setFont('Visible','off')
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
if dataMat(i,j) > 0
BCC.setChordMN(i,j, 'FaceAlpha',.6)
end
end
end
text(-1.2,1.2, 'a', 'FontName','Times New Roman', 'FontSize',35)
% =========================================================================
ax2 = axes('Parent',gcf, 'Position',[1/2,1/2,1/2,1/2]);
dataMat = rand([9,9]);
dataMat(dataMat > .4) = 0;
dataMat = dataMat.*(1:9);
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList);
BCC = BCC.draw();
BCC.tickState('on')
BCC.setFont('Visible','off')
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
if dataMat(i,j) > 0
BCC.setChordMN(i,j, 'FaceAlpha',.6)
end
end
end
text(-1.2,1.2, 'b', 'FontName','Times New Roman', 'FontSize',35)
% =========================================================================
ax3 = axes('Parent',gcf, 'Position',[0,0,1/2,1/2]);
dataMat = rand([9,9]);
dataMat(dataMat > .4) = 0;
dataMat = dataMat.*(1:9).';
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList);
BCC = BCC.draw();
BCC.tickState('on')
BCC.setFont('Visible','off')
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
if dataMat(i,j) > 0
BCC.setChordMN(i,j, 'FaceAlpha',.6)
end
end
end
text(-1.2,1.2, 'c', 'FontName','Times New Roman', 'FontSize',35)
% =========================================================================
ax4 = axes('Parent',gcf, 'Position',[1/2,0,1/2,1/2]);
ax4.XColor = 'none'; ax4.YColor = 'none';
ax4.XLim = [-1,1]; ax4.YLim = [-1,1];
hold on
NameList = {'Food supply', 'Biodiversity', 'Water quality regulation', ...
'Air quality regulation', 'Erosion control', 'Carbon storage', ...
'Water retention', 'Recreation', 'Soil quality regulation'};
patchHdl = [];
for i = 1:size(dataMat, 2)
patchHdl(i) = fill([10,11,12],[10,13,13], CList(i,:), 'EdgeColor',[0,0,0]);
end
lgdHdl = legend(patchHdl, NameList, 'Location','best', 'FontSize',14, 'FontName','Cambria', 'Box','off');
lgdHdl.Position = [.625,.11,.255,.27];
lgdHdl.ItemTokenSize = [18,8];
demo 16
dataMat = rand([15,15]);
dataMat(dataMat > .2) = 0;
CList = [ 75,146,241; 252,180, 65; 224, 64, 10; 5,100,146; 191,191,191;
26, 59,105; 255,227,130; 18,156,221; 202,107, 75; 0, 92,219;
243,210,136; 80, 99,129; 241,185,168; 224,131, 10; 120,147,190]./255;
CListC = [54,69,92]./255;
CList = CList.*.6 + CListC.*.4;
figure('Units','normalized', 'Position',[.02,.05,.6,.85])
BCC = biChordChart(dataMat, 'Arrow','on', 'CData',CList);
BCC = BCC.draw();
% 添加刻度
BCC.tickState('on')
% 修改字体,字号及颜色
BCC.setFont('FontName','Cambria', 'FontSize',17, 'Color',[0,0,0])
% 修改弦颜色(Modify chord color)
for i = 1:size(dataMat, 1)
for j = 1:size(dataMat, 2)
if dataMat(i,j) > 0
BCC.setChordMN(i,j, 'FaceColor',CListC ,'FaceAlpha',.07)
end
end
end
[~, N] = max(sum(dataMat > 0, 2));
for j = 1:size(dataMat, 2)
BCC.setChordMN(N,j, 'FaceColor',CList(N,:) ,'FaceAlpha',.6)
end
You need to download following tools:
I wanted to turn a Markdown nested list of text labels:
- A
- B
- C
- D
- G
- H
- E
- F
- Q
into a directed graph, like this:
Visualization of a directed graph representing a nest list of text labels
Here is my blog post with some related tips for doing this, including text I/O, text processing with patterns, and directed graph operations and visualization.
The topic recently came up in a MATLAB Central Answers forum thread, where community members discussed how to programmatically control when the end user can close a custom app. Imagine you need to prevent app closure during a critical process but want to allow the end user to close the app afterwards. This article will guide you through the steps to add this behavior to your app.
A demo is attached containing an app with a state button that, when enabled, disables the ability to close the app.
Steps
1. Add a property that stores the state of the closure as a scalar logical value. In this example, I named the property closeEnabled. The default value in this example is true, meaning that closing is enabled. -- How to add a property to an app in app designer
properties (Access = private)
closeEnabled = true % Flag that controls ability to close app
end
2. Add a CloseRequest function to the app figure. This function is called any time there is an attempt to close the app. Within the CloseRequest function, add a condition that deletes the app when closure is enabled. -- How to add a CloseRequest function to an app figure in app designer
function UIFigureCloseRequest(app, event)
if app.closeEnabled
delete(app)
end
3. Toggle the value of the closeEnabled property as needed in your code. Imagine you have a "Process" button that initiates a process where it is crucial for the app to remain open. Set the closeEnabled flag to false (closure is disabled) at the beginning of the button's callback function and then set it to true at the end (closure is enabled).
function ProcessButtonPress(app, event)
app.closeEnabled = false;
% MY PROCESS CODE
app.closeEnabled = true;
end
Handling Errors: There is one trap to keep in mind in the example above. What if something in the callback function breaks before the app.closeEnabled is returned to true? That leaves the app in a bad state where closure is blocked. A pro move would be to use a cleanupObj to manage returning the property to true. In the example below, the task to return the closeEnabled property to true is managed by the cleanup object, which will execute that command when execution is terminated in the ProcessButtonPress function—whether execution was terminated by error or by gracefully exiting the function.
function ProcessButtonPress(app, event)
app.closeEnabled = false;
cleanupClosure = onCleanup(@()set(app,'closeEnabled',true));
% MY CODE
end
Force Closure: If the CloseRequest function is preventing an app from closing, here are a couple of ways to force a closure.
  1. If you have the app's handle, use delete(app) or close(app,'force'). This will also work on the app's figure handle.
  2. If you do not have the app's handle, you can use close('all','force') to close all figures or use findall(groot,'type','figure') to find the app's figure handle.
Me: If you have parallel code and you apply this trick that only requires changing one line then it might go faster.
Reddit user: I did and it made my code 3x faster
Not bad for just one line of code!
Which makes me wonder. Could it make your MATLAB program go faster too? If you have some MATLAB code that makes use of parallel constructs like parfor or parfeval then start up your parallel pool like this
parpool("Threads")
before running your program.
The worst that will happen is you get an error message and you'll send us a bug report....or maybe it doesn't speed up much at all....
....or maybe you'll be like the Reddit user and get 3x speed-up for 10 seconds work. It must be worth a try...after all, you're using parallel computing to make your code faster right? May as well go all the way.
In an artificial benchmark I tried, I got 10x speedup! More details in my recent blog post: Parallel computing in MATLAB: Have you tried ThreadPools yet? » The MATLAB Blog - MATLAB & Simulink
Give it a try and let me know how you get on.
John D'Errico
John D'Errico
Letzte Aktivitätam 4 Mär. 2025

What is a rough number? What can they be used for? Today I'll take you down a journey into the land of prime numbers (in MATLAB). But remember that a journey is not always about your destination, but about what you learn along the way. And so, while this will be all about primes, and specifically large primes, before we get there we need some background. That will start with rough numbers.
Rough numbers are what I would describe as wannabe primes. Almost primes, and even sometimes prime, but often not prime. They could've been prime, but may not quite make it to the top. (If you are thinking of Marlon Brando here, telling us he "could've been a contender", you are on the right track.)
Mathematically, we could call a number k-rough if it is evenly divisible by no prime smaller than k. (Some authors will use the term k-rough to denote a number where the smallest prime factor is GREATER than k. The difference here is a minor one, and inconsequential for my purposes.) And there are also smooth numbers, numerical antagonists to the rough ones, those numbers with only small prime factors. They are not relevant to the topic today, even though smooth numbers are terribly valuable tools in mathematics. Please forward my apologies to the smooth numbers.
Have you seen rough numbers in use before? Probably so, at least if you ever learned about the sieve of Eratosthenes for prime numbers, though probably the concept of roughness was never explicitly discussed at the time. The sieve is simple. Suppose you wanted a list of all primes less than 100? (Without using the primes function itself.)
% simple sieve of Eratosthenes
Nmax = 100;
N = true(1,Nmax); % A boolean vector which when done, will indicate primes
N(1) = false; % 1 is not a prime by definition
nextP = find(N,1,'first'); % the first prime is 2
while nextP <= sqrt(Nmax)
% flag multiples of nextP as not prime
N(nextP*nextP:nextP:end) = false;
% find the first element after nextP that remains true
nextP = nextP + find(N(nextP+1:end),1,'first');
end
primeList = find(N)
primeList = 1×25
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Indeed, that is the set of all 25 primes not exceeding 100. If you think about how the sieve worked, it first found 2 is prime. Then it discarded all integer multiples of 2. The first element after 2 that remains as true is 3. 3 is of course the second prime. At each pass through the loop, the true elements that remain correspond to numbers which are becoming more and more rough. By the time we have eliminated all multiples of 2, 3, 5, and finally 7, everything else that remains below 100 must be prime! The next prime on the list we would find is 11, but we have already removed all multiples of 11 that do not exceed 100, since 11^2=121. For example, 77 is 11*7, but we already removed it, because 77 is a multiple of 7.
Such a simple sieve to find primes is great for small primes. However is not remotely useful in terms of finding primes with many thousands or even millions of decimal digits. And that is where I want to go, eventually. So how might we use roughness in a useful way? You can think of roughness as a way to increase the relative density of primes. That is, all primes are rough numbers. In fact, they are maximally rough. But not all rough numbers are primes. We might think of roughness as a necessary, but not sufficient condition to be prime.
How many primes lie in the interval [1e6,2e6]?
numel(primes(2e6)) - numel(primes(1e6))
ans = 70435
There are 70435 primes greater than 1e6, but less than 2e6. Given there are 1 million natural numbers in that set, roughly 7% of those numbers were prime. Next, how many 100-rough numbers lie in that same interval?
N = (1e6:2e6)';
roughInd = all(mod(N,primes(100)) > 0,2);
sum(roughInd)
ans = 120571
That is, there are 120571 100-rough numbers in that interval, but all those 70435 primes form a subset of the 100-rough numbers. What does this tell us? Of the 1 million numbers in that interval, approximately 12% of them were 100-rough, but 58% of the rough set were prime.
The point being, if we can efficiently identify a number as being rough, then we can substantially increase the chance it is also prime. Roughness in this sense is a prime densifier. (Is that even a word? It is now.) If we can reduce the number of times we need to perform an explicit isprime test, that will gain greatly because a direct test for primality is often quite costly in CPU time, at least on really large numbers.
In my next post, I'll show some ways we can employ rough numbers to look for some large primes.
The GCD approach to identify rough numbers is a terribly useful one, well worth remembering. But at some point, I expect someone to notice that all work done with these massively large symbolic numbers uses only one of the cores on your computer. And, having spent so much money on those extra cores in your CPU, surely we can find a way to use them all? The problem is, computations done on symbolic integers never use more than 1 core. (Sad, unhappy face.)
In order to use all of the power available to your computer using MATLAB, you need to work in double precision, or perhaps int64 or uint64. To do that, I'll next search for primes among the family 3^n+4. In fact, they seem pretty common, at least if we look at the first few such examples.
F = @(n) sym(3).^n + 4;
F(0:16)
ans =
[5, 7, 13, 31, 85, 247, 733, 2191, 6565, 19687, 59053, 177151, 531445, 1594327, 4782973, 14348911, 43046725]
isprime(F(0:16))
ans =
1×17 logical array
1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 0
Of the first 11 members of that sequence, 7 of them were prime. Naturally, primes will become less frequent in this sequence as we look further out. The members of this family grow rapidly in size. F(10000) has 4771 decimal digits, and F(100000) has 47712 decimal digits. We certainly don't want to directly test every member of that sequence for primality. However, what I will call a partial or incomplete sieve can greatly decrease the work needed.
Consider there are roughly 5.7 million primes less than 1e8.
numel(primes(1e8))
ans =
5761455
F(17) is the first member of our sequence that exceeds 1e8. So we can start there, since we already know the small-ish primes in this sequence.
roughlim = 1e8;
primes1e8 = primes(roughlim);
primes1e8([1 2]) = []; % F(n) is never divisible by 2 or 3
F_17 = double(F(17));
Fremainders = mod(F_17,primes1e8);
nmax = 100000;
FnIsRough = false(1,nmax);
for n = 17:nmax
if all(Fremainders)
FnIsRough(n) = true;
end
% update the remainders for the next term in the sequence
% This uses the recursion: F(n+1) = 3*F(n) - 8
Fremainders = mod(Fremainders*3 - 8,primes1e8);
end
sum(FnIsRough)
ans =
6876
These will be effectively trial divides, even though we use mod for the purpose. The result is 6876 1e8-rough numbers, far less than that total set of 99984 values for n. One thing of great importance is to recognize this sequence of tests will use an approximately constant time per test regardless of the size of the numbers because each test works off the remainders from the previous one. And that works as long as we can update those remainders in some simple, direct, and efficient fashion. All that matters is the size of the set of primes to test against. Remember, the beauty of this scheme is that while I did what are implicitly trial divides against 5.76 million primes at each step, ALL of the work was done in double precision. That means I used all 8 of the cores on my computer, pushing them as hard as I could. I never had to go into the realm of big integer arithmetic to identify the rough members in that sequence, and by staying in the realm of doubles, MATLAB will automatically use all the cores you have available.
The first 10 values of n (where n is at least 17), such that F(n) is 1e8-rough were
FnIsRough = find(FnIsRough);
FnIsRough(1:10)
ans =
22 30 42 57 87 94 166 174 195 198
How well does the roughness test do to eliminate composite members of this sequence?
isprime(F(FnIsRough(1:10)))
ans =
1×10 logical array
1 1 1 1 1 0 0 1 1 1
As you can see, 8 of those first few 1e8-rough members were actually prime, so only 2 of those eventual isprime tests were effectively wasted. That means the roughness test was quite useful indeed as an efficient but relatively weak pre-test for possible primality. More importantly it is a way to quickly eliminate those values which can be known to be composite.
You can apply a similar set of tests on many families of numbers. For example, repunit primes are a great case. A rep-digit number is any number composed of a sequence of only a single digit, like 11, 777, and 9999999999999.
However, you should understand that only rep-digit numbers composed of entirely ones can ever be prime. Naturally, any number composed entirely of the digit D, will always be divisible by the single digit number D, and so only rep-unit numbers can be prime. Repunit numbers are a subset of the rep-digit family, so numbers composed only of a string of ones. 11 is the first such repunit prime. We can write them in MATLAB as a simple expression:
RU = @(N) (sym(10).^N - 1)/9;
RU(N) is a number composed only of the digit 1, with N decimal digits. This family also follows a recurrence relation, and so we could use a similar scheme as was used to find rough members of the set 3^N-4.
RU(N+1) == 10*RU(N) + 1
However, repunit numbers are rarely prime. Looking out as far as 500 digit repunit numbers, we would see primes are pretty scarce in this specific family.
find(isprime(RU(1:500)))
ans =
2 19 23 317
There are of course good reasons why repunit numbers are rarely prime. One of them is they can only ever be prime when the number of digits is also prime. This is easy to show, as you can always factor any repunit number with a composite number of digits in a simple way:
1111 (4 digits) = 11*101
111111111 (9 digits) = 111*1001001
Finally, I'll mention that Mersenne primes are indeed another example of repunit primes, when expressed in base 2. A fun fact: a Mersenne number of the form 2^n-1, when n is prime, can only have prime factors of the form 1+2*k*n. Even the Mersenne number itself will be of the same general form. And remember that a Mersenne number M(n) can only ever be prime when n is itself prime. Try it! For example, 11 is prime.
Mn = @(n) sym(2).^n - 1;
Mn(11)
ans =
2047
Note that 2047 = 1 + 186*11. But M(11) is not itself prime.
factor(Mn(11))
ans =
[23, 89]
Looking carefully at both of those factors, we see that 23 == 1+2*11, and 89 = 1+8*11.
How does this help us? Perhaps you may see where this is going. The largest known Mersenne prime at this date is Mn(136279841). This is one seriously massive prime, containing 41,024,320 decimal digits. I have no plans to directly test numbers of that size for primality here, at least not with my current computing capacity. Regardless, even at that realm of immensity, we can still do something.
If the largest known Mersenne prime comes from n=136279841, then the next such prime must have a larger prime exponent. What are the next few primes that exceed 136279841?
np = NaN(1,11); np(1) = 136279841;
for i = 1:10
np(i+1) = nextprime(np(i)+1);
end
np(1) = [];
np
np =
Columns 1 through 8
136279879 136279901 136279919 136279933 136279967 136279981 136279987 136280003
Columns 9 through 10
136280009 136280051
The next 10 candidates for Mersenne primality lie in the set Mn(np), though it is unlikely that any of those Mersenne numbers will be prime. But ... is it possible that any of them may form the next Mersenne prime? At the very least, we can exclude a few of them.
for i = 1:10
2*find(powermod(sym(2),np(i),1+2*(1:50000)*np(i))==1)
end
ans =
18 40 64
ans =
1×0 empty double row vector
ans =
2
ans =
1×0 empty double row vector
ans =
1×0 empty double row vector
ans =
1×0 empty double row vector
ans =
1×0 empty double row vector
ans =
1×0 empty double row vector
ans =
1×0 empty double row vector
ans =
2
Even with this quick test which took only a few seconds to run on my computer, we see that 3 of those Mersenne numbers are clearly not prime. In fact, we already know three of the factors of M(136279879), as 1+[18,40,64]*136279879.
You might ask, when is the MOD style test, using a large scale test for roughness against many thousands or millions of small primes, when is it better than the use of GCD? The answer here is clear. Use the large scale mod test when you can easily move from one member of the family to the next, typically using a linear recurrence. Simple such examples of this are:
1. Repunit numbers
General form: R(n) = (10^n-1)/9
Recurrence: R(n+1) = 10*R(n) + 1, R(0) = 1, R(1) = 11
2. Fibonacci numbers.
Recurrence: F(n+1) = F(n) + F(n-1), F(0) = 0, F(1) = 1
3. Mersenne numbers.
General form: M(n) = 2^n - 1
Recurrence: M(n+1) = 2*M(n) + 1
General form: C(n) = n*2^n + 1
Recurrence: C(n+1) = 4*C(n) + 4*C(n-1) + 1
5. Hampshire numbers: (My own choice of name)
General form: H(n,b) = (n+1)*b^n - 1
Recurrence: H(n+1,b) = 2*b*H(n-1,b) - b^2*H(n-2,b) - (b-1)^2, H(0,b) = 0, H(1,b) = 2*b-1
6. Tin numbers, so named because Sn is the atomic symbol for tin.
General form: S(n) = 2*n*F(n) + 1, where F(n) is the nth Fibonacci number.
Recurrence: S(n) = S(n-5) + S(n-4) - 3*S(n-3) - S(n-2) +3*S(n-1);
To wrap thing up, I hope you have enjoyed this beginning of a journey into large primes and non-primes. I've shown a few ways we can use roughness, first in a constructive way to identify numbers which may harbor primes in a greater density than would otherwise be expected. Next, using GCD in a very pretty way, and finally by use of MOD and the full power of MATLAB to test elements of a sequence of numbers for potential primality.
My next post will delve into the world of Fermat and his little theorem, showing how it can be used as a stronger test for primality (though not perfect.)

Info zu Tips & Tricks

Looking to improve your skills and efficiency? Reach your coding goals quickly and more easily with how-tos, tutorials, and shortcuts posted by our staff and community power users.

Do you have your own tips and tricks to share? Help other community users with your insights and experience.

Mitwirkende mit den besten Beiträgen

Moderatoren