Files
OpenNest-Engines/OpenNest.Engine.Gpt6Astra/benchmarks/Program.cs
T
ajandClaude Opus 5.5 755ea4d7f8 refactor: rename Astra engine to Gpt6Astra
OpenAI reuses model codenames across generations (GPT-6 reused
GPT-5.6's Sol and Luna), so a bare codename like "Astra" can't
identify which model built the engine. Prefixing the model version
keeps engine names unambiguous as more runs are added. The CLR type
is now Gpt6AstraNestingEngine, so benchmark reports show the new name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 10:36:00 -04:00

95 lines
6.2 KiB
C#

using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.Loader;
using OpenNest;
using OpenNest.CNC;
using OpenNest.Engine.Jobs;
using OpenNest.Engine.Jobs.Adapters;
using OpenNest.Geometry;
using OpenNest.Benchmark;
using CncProgram = OpenNest.CNC.Program;
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
if (args.Contains("--diagnose-ring"))
{
var ringPart = new NestJobPart("ring", PartGeometrySnapshot.FromProgram(
new OpenNest.Shapes.RingShape { OuterDiameter = 10, InnerDiameter = 7 }.GetDrawing().Program), 1);
var insertPart = new NestJobPart("insert", PartGeometrySnapshot.FromProgram(
new OpenNest.Shapes.CircleShape { Diameter = 6 }.GetDrawing().Program), 1);
foreach (var quadrant in new[] { 1, 2 })
foreach (var offset in new[] { 0.0, 0.0003, 0.05, 0.1 })
{
var s = new NestPlateStock("s", new Size(24, 42), 1, 0.175,
new Spacing(0.2, 0.3, 0.4, 0.5), quadrant);
var j = new NestJob(new[] { ringPart, insertPart }, new[] { s });
var x = (quadrant == 1 ? 0 : -42) + s.EdgeSpacing.Left + 5;
var y = s.EdgeSpacing.Bottom + 5;
var result = new NestJobResult(NestJobStatus.Complete, NestJobStopReason.Completed,
new[] { new NestJobPlateResult(0, s, new[] { new NestJobPlacement("ring", 0, x, y, 0),
new NestJobPlacement("insert", 0, x + offset, y, 0) }) },
new[] { new PartFulfillment("ring", 1, 1, 0), new PartFulfillment("insert", 1, 1, 0) },
new[] { new StockUsage("s", 1, 0) });
var materialized = NestResultMaterializer.Materialize(j, result);
var check = NestValidator.Validate(materialized.Nest.Plates.Select(p => (p, p.Parts.ToList())).ToList(),
j.Parts.ToDictionary(p => materialized.DrawingsByPartId[p.Id], p => (p.Id, p.Quantity)));
Console.WriteLine($"q={quadrant} offset={offset} valid={check.Valid}: {string.Join(';', check.Violations)}");
}
return;
}
var assembly = args.Length > 0 && args[0].EndsWith(".dll")
? new AssemblyLoadContext("benchmark-plugin", isCollectible: true).LoadFromAssemblyPath(Path.GetFullPath(args[0])) : Assembly.Load("OpenNest.Engine.Gpt6Astra");
var engine = (INestingEngine)Activator.CreateInstance(assembly.GetType("OpenNest.Engine.Gpt6Astra.Gpt6AstraNestingEngine")!);
var cases = new List<(string Name, NestJob Job)>();
var standard = new[] { new NestPlateStock("small", new Size(24, 48), partSpacing: 0.15),
new NestPlateStock("large", new Size(48, 96), partSpacing: 0.15) };
NestJobPart Part(string name, CncProgram p, int count, RotationPolicy rotation = null) =>
new(name, PartGeometrySnapshot.FromProgram(p), count, rotation: rotation);
CncProgram Polygon(params double[] xy)
{
var p = new CncProgram(); p.MoveTo(xy[0], xy[1]);
for (var i = 2; i < xy.Length; i += 2) p.LineTo(xy[i], xy[i + 1]);
p.LineTo(xy[0], xy[1]); return p;
}
CncProgram Rect(double w, double h) => Polygon(0, 0, w, 0, w, h, 0, h);
CncProgram Circle(double r) { var p = new CncProgram(); p.MoveTo(r, 0); p.ArcTo(r, 0, 0, 0, RotationType.CCW); return p; }
void Add(string name, NestJobPart[] parts, NestPlateStock[] stocks = null, NestJobOptions options = null) =>
cases.Add((name, new NestJob(parts, stocks ?? standard, options)));
Add("triangles", new[] { Part("triangle", Polygon(0, 0, 10, 0, 0, 10), 60) });
Add("circles", new[] { Part("circle", Circle(2.5), 90) });
Add("circles-dense", new[] { Part("circle", Circle(2.5), 80) });
Add("concave-L", new[] { Part("L", Polygon(0, 0, 8, 0, 8, 2, 2, 2, 2, 8, 0, 8), 60) });
Add("mixed", new[] { Part("rect", Rect(9, 4), 30), Part("triangle", Polygon(0, 0, 8, 0, 3, 6), 25),
Part("circle", Circle(2), 30), Part("L", Polygon(0, 0, 7, 0, 7, 2, 2, 2, 2, 6, 0, 6), 20) });
var ring = Rect(10, 10); ring.MoveTo(1, 1); ring.LineTo(1, 9); ring.LineTo(9, 9); ring.LineTo(9, 1); ring.LineTo(1, 1);
Add("holes", new[] { Part("frame", ring, 8), Part("insert", Rect(7, 7), 8) });
Add("rectangles", Enumerable.Range(0, 8).Select(i => Part($"r{i}", Rect(2 + i, 3 + i % 3), 12)).ToArray());
Add("grain", new[] { Part("fixed", Rect(13, 3), 20, RotationPolicy.Fixed(System.Math.PI / 6)),
Part("sweep", Rect(7, 2), 40, RotationPolicy.BoundedSweep(0, System.Math.PI / 2, System.Math.PI / 4)) });
Add("scarce-stock", new[] { Part("small", Rect(4, 4), 8), Part("large", Rect(10, 10), 1) },
new[] { new NestPlateStock("scarce", new Size(10, 10), 1), new NestPlateStock("small-only", new Size(4, 8)) });
Add("tail", new[] { Part("rect", Rect(6, 4), 17) }, new[] {
new NestPlateStock("small", new Size(8, 12), partSpacing: 0.1), new NestPlateStock("large", new Size(20, 30), partSpacing: 0.1) });
Add("plate-cap", new[] { Part("r", Rect(5, 5), 10) }, new[] {
new NestPlateStock("small", new Size(10, 10)), new NestPlateStock("large", new Size(20, 20)) }, new NestJobOptions(maxPlates: 1));
cases.AddRange(OpenNest.Engine.Gpt6Astra.Benchmarks.GeneratedCases.Create());
Console.WriteLine("case,valid,placed,requested,sheets,area,milliseconds");
foreach (var (name, job) in cases)
{
if (args.Length > 1 && !name.Contains(args[1], StringComparison.OrdinalIgnoreCase)) continue;
var sw = Stopwatch.StartNew();
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(90));
try
{
var result = engine.Solve(job, token: cts.Token); sw.Stop();
var nest = NestResultMaterializer.Materialize(job, result);
var validation = NestValidator.Validate(nest.Nest.Plates.Select(p => (p, p.Parts.ToList())).ToList(),
job.Parts.ToDictionary(p => nest.DrawingsByPartId[p.Id], p => (p.Id, p.Quantity)));
NestValidator.ValidateAgainstJob(job, result, job.Parts.ToDictionary(p => p.Id, p => p.Id), validation);
if (!validation.Valid) Environment.ExitCode = 1;
Console.WriteLine($"{name},{validation.Valid},{result.Fulfillment.Sum(f => f.Placed)},{job.Parts.Sum(p => p.Quantity)},{result.Plates.Count},{result.Plates.Sum(p => p.Stock.Size.Length * p.Stock.Size.Width)},{sw.ElapsedMilliseconds}");
foreach (var violation in validation.Violations.Take(4)) Console.Error.WriteLine($"{name}: {violation}");
}
catch (Exception ex) { Environment.ExitCode = 1; Console.WriteLine($"{name},ERROR,,,,,{sw.ElapsedMilliseconds}"); Console.Error.WriteLine(ex); }
}