{"id":101,"date":"2025-10-17T20:49:42","date_gmt":"2025-10-18T00:49:42","guid":{"rendered":"https:\/\/minax.com\/blog\/?p=101"},"modified":"2025-10-22T08:25:28","modified_gmt":"2025-10-22T12:25:28","slug":"deswik-mine-design-optimization-with-aid-of-ai","status":"publish","type":"post","link":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/","title":{"rendered":"Deswik Mine Design Optimization with Aid of AI"},"content":{"rendered":"\n<p>In last week&#8217;s Friday Deswik Challenge, I posted the following question on our LinkedIn page (original post link here: <a href=\"https:\/\/www.linkedin.com\/feed\/update\/urn:li:activity:7382496943568171009\/\">Post | Feed | LinkedIn<\/a>):<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Assume you have 1,200 stopes designed using Stope Optimizer across 20 mining levels. They all have unique stope ID from SO. These stopes all have varying gold grade\/value. Each stope is designed to be a slice of 4 metres long. You need to select a group of these stopes to merge to create a stope no more than 20 metres long because a 4-metre rib pillar would be required between stopes. The optimum grouping would result to minimum amount of gold oz (tonnes multiplied by grade) left in pillars. How would you group stopes and pillars? Please provide proof you have the optimum results.<\/p>\n<\/blockquote>\n\n\n\n<p>This is a replica of a real-world challenge our team faced in our <a href=\"https:\/\/minax.com\/\" title=\"\">consulting practice<\/a>. While there are several different approaches to it, we&#8217;d like to share an approach that turns the CAD problem into a mathematical one that can be solved. While I do not expect every mine engineer out there to be a programmer, so I shared an approach leveraging AI power so that anyone without programming experience could solve a complex design issue like this one. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Problem Statement in Deswik <\/h2>\n\n\n\n<p>Let&#8217;s visually present the challenge in Deswik. <\/p>\n\n\n\n<p>The image below shows on a typical plan view of all stopes along with the access and sill drifts. Red\/green\/blue boxes represent 4-metre-long slices. Each box has their own metal content. What combination of these slices will result in the least amount of metal left in rib pillars?<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"864\" height=\"423\" src=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-3.png\" alt=\"\" class=\"wp-image-106\" srcset=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-3.png 864w, https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-3-300x147.png 300w, https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-3-768x376.png 768w\" sizes=\"auto, (max-width: 864px) 100vw, 864px\" \/><figcaption class=\"wp-element-caption\">Challenge: Combine stopes to be 20m or less with pillars in between. Minimize oz loss in pillars<\/figcaption><\/figure>\n\n\n\n<p>Then, we thought of Deswik macro scripts to the rescue. While interested in understand AI&#8217;s capabilities in writing custom script in Deswik environment, we asked ChatGPT the following question.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Think Deswik Macro and Resort to ChatGPT<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>I have 2000 design solids in Deswik. Each design solid contains attributes of name, gold ounce, tonnes, level, vein name. Each solid is 4 metres long. I need to merge continuous solids so that they are not longer than 20 metres. Between 2 adjacent shapes, a 4-metre solid needs to be untouched as a pillar. The goal is to minimize the total gold ounces left in the pillars. Write a script in Deswik Macro to do that.<\/p>\n<\/blockquote>\n\n\n\n<p>After 31 seconds of thinking, ChatGPT returned with the following response:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Got it \u2014 here\u2019s a practical way to do this in Deswik with a macro-driven workflow that (a) picks the <em>best<\/em> 4 m pillars to minimize lost ounces and (b) merges stopes up to 20 m (5 solids) between those pillars.<\/p>\n\n\n\n<p>I\u2019ll give you:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>the algorithm (so it\u2019s clear and auditable),<\/li>\n\n\n\n<li>a Deswik Macro (pseudo-VB style that maps 1:1 to common Deswik macro commands and attribute ops), and<\/li>\n\n\n\n<li>the post-processing steps (boolean merge by attribute) you can run right in Deswik.<\/li>\n<\/ol>\n\n\n\n<p>If your solids already have a clean \u201cchain order\u201d along strike, the macro will use it. If not, it will project centroids onto a user-defined strike vector to create one.<\/p>\n<\/blockquote>\n\n\n\n<p>I was pretty impressed by two things: <\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>how well ChatGPT understood the context and even adds a strike vector or Chain attribute to control direction of grouping. <\/li>\n\n\n\n<li>It understood Deswik operation at a reasonable level (e.g. macro style, solid boolean merge, etc.).<\/li>\n<\/ol>\n\n\n\n<p>The logic it presents is solid, shown below. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<h3 class=\"wp-block-heading\">Optimization logic (per Vein + Level chain)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Each solid = 4 m along strike.<\/li>\n\n\n\n<li>A stope is a <strong>run of 1\u20135 solids<\/strong> (max 20 m).<\/li>\n\n\n\n<li>Between two adjacent stopes, <strong>exactly one solid<\/strong> must be left as a <strong>pillar<\/strong> (4 m).<\/li>\n\n\n\n<li>Goal: <strong>minimize gold left in pillars<\/strong> \u21d4 <strong>maximize gold in stopes<\/strong>.<\/li>\n<\/ul>\n\n\n\n<p>Dynamic programming on an ordered list of solids (index <code>i<\/code> from 0\u2026n\u22121):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Let gold&#91;i] = Au ounces in solid i.\n\ndp&#91;i] = maximum Au we can include from i..end\nchoice&#91;i] = best stope length L (1..5) to take at i\n\nTransition:\nFor L in 1..5:\n  if i+L &lt;= n:\n     take solids &#91;i .. i+L-1] as a stope\n     next_i = i+L\n     if next_i &lt; n:   # there are solids left\n         # leave one 4 m pillar\n         next_i = next_i + 1\n     candidate = sum(gold&#91;i .. i+L-1]) + dp&#91;next_i]\n     dp&#91;i] = max over candidates\nBase:\ndp&#91;n] = 0\n<\/code><\/pre>\n\n\n\n<p>Recover choices to mark:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pillars = the skipped single solids after each chosen stope (except at the very end if we end exactly at <code>n<\/code>).<\/li>\n\n\n\n<li>Assign a <code>StopeId<\/code> to each chosen stope segment; each stope\u2019s length \u2264 5 solids by construction.<\/li>\n<\/ul>\n<\/blockquote>\n\n\n\n<p>Then it proceeded to the step of creating a VB-style macro for Deswik. Here&#8217;s the initial code it generated:<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>This macro assumes your solids have attributes:<br><code>NAME<\/code> (string)<br><code>VEIN<\/code> (string)<br><code>LEVEL<\/code> (string)<br><code>AU_OZ<\/code> (double) \u2190 ounces<br>(Optional) <code>CHAIN<\/code> (double\/int) \u2190 order along strike. If missing, we\u2019ll compute <code>CHAIN<\/code> by projection.<br>The macro writes:<br><code>IS_PILLAR<\/code> (int: 0\/1)<br><code>STOPE_ID<\/code> (string)<br><code>STOPE_LEN_M<\/code> (double) on <em>each<\/em> solid that belongs to a stope (0 on pillars)<br><code>CHAIN<\/code> if it had to compute order<br>Drop this in your Macro Editor. The macro uses only attribute read\/writes and common selection\/query ops. Where your Deswik build names commands slightly differently, adjust the <code>Command(...)<\/code> call names to match your menu commands.<\/summary>\n<pre class=\"wp-block-code\"><code>' ============================================\n' MergeStopesWithOptimizedPillars.DeskMacro\n' ============================================\n\n' === USER PARAMETERS ===\nConst SOLID_LEN_M = 4.0\nConst MAX_STOPE_M = 20.0\nConst MAX_STOPE_SOLIDS = 5   ' 20 m \/ 4 m\nConst ATTR_NAME   = \"NAME\"\nConst ATTR_VEIN   = \"VEIN\"\nConst ATTR_LEVEL  = \"LEVEL\"\nConst ATTR_AUOZ   = \"AU_OZ\"\nConst ATTR_CHAIN  = \"CHAIN\"      ' optional, will compute if missing\nConst ATTR_ISPIL  = \"IS_PILLAR\"  ' 0\/1\nConst ATTR_STOPE  = \"STOPE_ID\"\nConst ATTR_LENM   = \"STOPE_LEN_M\"\n\n' === STEP 0: INPUT SELECTION ===\nDim solids\nsolids = GetSelection(\"Select the 4 m design solids to process\")\n\nIf Count(solids) = 0 Then\n    MsgBox \"No solids selected.\"\n    Exit Sub\nEnd If\n\n' === STEP 1: Ensure grouping and ordering keys are available ===\n' Group key: (VEIN, LEVEL)\n' Order key: CHAIN (if missing, compute projection onto user-picked strike)\n\nDim haveChain : haveChain = AttributeExists(solids, ATTR_CHAIN)\n\nIf Not haveChain Then\n    MsgBox \"CHAIN attribute not found; please pick two points along strike to define ordering.\"\n    Dim p1, p2\n    p1 = PickPoint(\"Pick strike start point\")\n    p2 = PickPoint(\"Pick strike end point\")\n    Dim vx, vy, vz\n    vx = p2.x - p1.x : vy = p2.y - p1.y : vz = p2.z - p1.z\n    Dim vlen : vlen = Sqr(vx*vx + vy*vy + vz*vz)\n    If vlen = 0 Then\n        MsgBox \"Invalid strike vector.\"\n        Exit Sub\n    End If\n    vx = vx \/ vlen : vy = vy \/ vlen : vz = vz \/ vlen\n\n    ' Compute CHAIN = dot(centroid - p1, v)\n    Dim e\n    For Each e In solids\n        Dim c : c = GetCentroid(e)\n        Dim dx, dy, dz\n        dx = c.x - p1.x : dy = c.y - p1.y : dz = c.z - p1.z\n        Dim chainVal : chainVal = dx*vx + dy*vy + dz*vz\n        SetAttribute e, ATTR_CHAIN, chainVal\n    Next\nEnd If\n\n' === STEP 2: Normalize outputs ===\nDim s\nFor Each s In solids\n    SetAttribute s, ATTR_ISPIL, 0\n    SetAttribute s, ATTR_STOPE, \"\"\n    SetAttribute s, ATTR_LENM,  0.0\nNext\n\n' === STEP 3: Group by (VEIN, LEVEL) ===\nDim groups : Set groups = CreateDictionary() ' key = vein || level, value = list of entity ids\nFor Each s In solids\n    Dim vein : vein = GetAttribute(s, ATTR_VEIN)\n    Dim lev  : lev  = GetAttribute(s, ATTR_LEVEL)\n    Dim key  : key  = vein &amp; \"||\" &amp; lev\n    DictAppend groups, key, s\nNext\n\n' === STEP 4: Process each group (sort, DP, mark pillars\/stopes) ===\nDim gkey\nFor Each gkey In DictKeys(groups)\n    Dim lst : lst = DictGet(groups, gkey)\n\n    ' sort by CHAIN ascending\n    lst = SortByAttribute(lst, ATTR_CHAIN, True)\n\n    ' collect ounces and ids\n    Dim n : n = Count(lst)\n    If n = 0 Then GoTo NextGroup\n\n    Dim gold() : ReDim gold(n-1)\n    Dim ids()  : ReDim ids(n-1)\n\n    Dim i\n    For i = 0 To n-1\n        ids(i)  = lst(i)\n        gold(i) = CDbl(GetAttribute(lst(i), ATTR_AUOZ))\n    Next\n\n    ' DP arrays\n    Dim dp()     : ReDim dp(n)\n    Dim choice() : ReDim choice(n-1)\n\n    dp(n) = 0\n    For i = n-1 To 0 Step -1\n        Dim best, bestL, L\n        best = -1.0E+30\n        bestL = 1\n        For L = 1 To MAX_STOPE_SOLIDS\n            If i + L &lt;= n Then\n                Dim sumL : sumL = SumRange(gold, i, i+L-1)\n                Dim nexti : nexti = i + L\n                If nexti &lt; n Then\n                    nexti = nexti + 1 ' leave 1 pillar\n                End If\n                Dim cand : cand = sumL + dp(nexti)\n                If cand &gt; best Then\n                    best  = cand\n                    bestL = L\n                End If\n            End If\n        Next\n        dp(i) = best\n        choice(i) = bestL\n    Next\n\n    ' Walk choices, mark attributes\n    Dim stopeIdx : stopeIdx = 1\n    i = 0\n    Do While i &lt; n\n        Dim L : L = choice(i)\n        ' Mark stope solids &#91;i .. i+L-1]\n        Dim stopeId : stopeId = gkey &amp; \"_S\" &amp; CStr(stopeIdx)\n        Dim j\n        For j = i To i+L-1\n            SetAttribute ids(j), ATTR_STOPE, stopeId\n        Next\n        ' store stope length on members (for reference)\n        Dim lenM : lenM = L * SOLID_LEN_M\n        For j = i To i+L-1\n            SetAttribute ids(j), ATTR_LENM, lenM\n        Next\n        stopeIdx = stopeIdx + 1\n\n        i = i + L\n        ' Leave one pillar if not at end\n        If i &lt; n Then\n            SetAttribute ids(i), ATTR_ISPIL, 1\n            ' Pillar has no stope id\/len\n            SetAttribute ids(i), ATTR_STOPE, \"\"\n            SetAttribute ids(i), ATTR_LENM, 0.0\n            i = i + 1\n        End If\n    Loop\n\nNextGroup:\nNext\n\n' === STEP 5: Report totals by group (optional) ===\nDim totalPillarOz : totalPillarOz = 0#\nFor Each s In solids\n    If CInt(GetAttribute(s, ATTR_ISPIL)) = 1 Then\n        totalPillarOz = totalPillarOz + CDbl(GetAttribute(s, ATTR_AUOZ))\n    End If\nNext\nMsgBox \"Finished. Total ounces left in pillars: \" &amp; FormatNumber(totalPillarOz, 2)\n\n' === STEP 6: (Run manually after macro) Boolean-merge stopes by STOPE_ID ===\n' Use your standard Deswik command:\n'   Solids \u2192 Boolean \u2192 Union by Attribute\n'   Grouping attribute: STOPE_ID\n' This will merge each stope\u2019s 1\u20135 solids into a single \u226420 m solid.\n\n' ================== helpers ==================\nFunction AttributeExists(entities, attrName)\n    Dim e\n    For Each e In entities\n        If HasAttribute(e, attrName) Then\n            AttributeExists = True\n            Exit Function\n        End If\n    Next\n    AttributeExists = False\nEnd Function\n\nFunction DictKeys(d)\n    DictKeys = d.Keys\nEnd Function\n\nSub DictAppend(d, k, v)\n    If Not d.Exists(k) Then\n        Dim tmp : Set tmp = CreateList()\n        d.Add k, tmp\n    End If\n    d.Item(k).Add v\nEnd Sub\n\nFunction DictGet(d, k)\n    DictGet = d.Item(k)\nEnd Function\n\nFunction SumRange(arr, a, b)\n    Dim s : s = 0#\n    Dim t\n    For t = a To b\n        s = s + arr(t)\n    Next\n    SumRange = s\nEnd Function\n<\/code><\/pre>\n<\/details>\n\n\n\n<p>I plugged the code into Deswik Macro Editor. Unfortunately, the code compiled with errors in Deswik and couldn&#8217;t get me any output for pillar selection. I asked ChatGPT several rounds of questions to tweak the code, only to no vail. Finally, ChatGPT sensed my frustration and disappointment and suggested a totally different workflow as the following:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error, Frustration with Deswik Macros: Pivot to Python<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Rather than burning more cycles fighting the host quirks, here\u2019s a <strong>reliable, tool-agnostic workflow<\/strong> that gets you the exact optimization you need and drops the result straight back into Deswik for the final boolean merge.<\/p>\n\n\n\n<p>I\u2019ve already created a ready-to-run Python script for you (with a download link).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What this does<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Implements the optimal dynamic-programming plan: stopes are runs of 1\u20135 solids (\u226420 m), with a <strong>single 4 m pillar between stopes<\/strong>, minimizing total gold left in pillars.<\/li>\n\n\n\n<li>Works <strong>per (VEIN, LEVEL)<\/strong> chain.<\/li>\n\n\n\n<li>Uses your <strong>AU_OZ<\/strong> values and either:\n<ul class=\"wp-block-list\">\n<li>an existing <strong>CHAIN<\/strong> column, or<\/li>\n\n\n\n<li>computes CHAIN from centroids using a strike vector you provide.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>It writes back three attributes for each solid:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>IS_PILLAR<\/code> (0\/1)<\/li>\n\n\n\n<li><code>STOPE_ID<\/code> (blank on pillars)<\/li>\n\n\n\n<li><code>STOPE_LEN_M<\/code> (0 on pillars; 4\/8\/12\/16\/20 on stope members)<\/li>\n<\/ul>\n<\/blockquote>\n\n\n\n<p>You can see the detailed Python code here.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Detailed Python code below.<\/summary>\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>#!\/usr\/bin\/env python3<\/p>\n\n\n\n<p>&#8220;&#8221;&#8221;<\/p>\n\n\n\n<p>Stope optimizer: choose 4 m pillars between merged stopes (&lt;= 20 m) to MINIMIZE gold left in pillars.<\/p>\n\n\n\n<p>&#8211; Works per (VEIN, LEVEL) chain.<\/p>\n\n\n\n<p>&#8211; Each stope is 1..5 solids (4 m each); between stopes, exactly 1 solid is a pillar (left unmerged).<\/p>\n\n\n\n<p>&#8211; Objective: maximize total Au in stopes (equivalently, minimize Au left in pillars).<\/p>\n\n\n\n<p>Input CSV requirements (headers are case-insensitive):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8211; ID &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: unique ID to join back in Deswik (NAME or GUID); required<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8211; VEIN &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: string; required<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8211; LEVEL &nbsp; &nbsp; &nbsp; &nbsp; : string; required<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8211; AU_OZ &nbsp; &nbsp; &nbsp; &nbsp; : numeric; required<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8211; CHAIN &nbsp; &nbsp; &nbsp; &nbsp; : numeric order along strike; optional<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8211; X,Y,Z &nbsp; &nbsp; &nbsp; &nbsp; : centroid coords for each solid; optional (only needed if CHAIN missing and you want to compute by strike)<\/p>\n\n\n\n<p>If CHAIN is missing, you must supply &#8211;strike &#8220;x1,y1,z1 x2,y2,z2&#8221; to compute CHAIN by projection.<\/p>\n\n\n\n<p>Usage:<\/p>\n\n\n\n<p>&nbsp; &nbsp; python stope_optimizer.py &#8211;in solids.csv &#8211;out stope_plan.csv &#8211;strike &#8220;x1,y1,z1 x2,y2,z2&#8221;<\/p>\n\n\n\n<p>&nbsp; &nbsp; # If CHAIN already exists in solids.csv, you can omit &#8211;strike.<\/p>\n\n\n\n<p>The output CSV will contain (in addition to original columns):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8211; IS_PILLAR (0\/1)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8211; STOPE_ID &nbsp;(string, blank on pillars)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8211; STOPE_LEN_M (numeric; 0 on pillars; in {4,8,12,16,20} on stopes)<\/p>\n\n\n\n<p>&#8220;&#8221;&#8221;<\/p>\n\n\n\n<p>import argparse<\/p>\n\n\n\n<p>import csv<\/p>\n\n\n\n<p>import math<\/p>\n\n\n\n<p>from collections import defaultdict<\/p>\n\n\n\n<p>MAX_STOPE_SOLIDS = 5<\/p>\n\n\n\n<p>SOLID_LEN_M = 4.0<\/p>\n\n\n\n<p>def parse_args():<\/p>\n\n\n\n<p>&nbsp; &nbsp; p = argparse.ArgumentParser(description=&#8221;Optimize stopes with mandatory single pillars between them.&#8221;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; p.add_argument(&#8216;&#8211;in&#8217;, dest=&#8217;in_csv&#8217;, required=True, help=&#8217;Input CSV of 4 m solids (exported from Deswik)&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; p.add_argument(&#8216;&#8211;out&#8217;, dest=&#8217;out_csv&#8217;, required=True, help=&#8217;Output CSV with pillar\/stope attributes&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; p.add_argument(&#8216;&#8211;strike&#8217;, dest=&#8217;strike&#8217;, default=None,<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;help=&#8217;Two 3D points for strike to compute CHAIN if missing, format: &#8220;x1,y1,z1 x2,y2,z2&#8243;&#8216;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; return p.parse_args()<\/p>\n\n\n\n<p>def norm(v):<\/p>\n\n\n\n<p>&nbsp; &nbsp; x,y,z = v<\/p>\n\n\n\n<p>&nbsp; &nbsp; m = math.sqrt(x*x + y*y + z*z)<\/p>\n\n\n\n<p>&nbsp; &nbsp; return (0.0,0.0,0.0) if m==0 else (x\/m, y\/m, z\/m)<\/p>\n\n\n\n<p>def dot(a,b):<\/p>\n\n\n\n<p>&nbsp; &nbsp; return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]<\/p>\n\n\n\n<p>def parse_strike(s):<\/p>\n\n\n\n<p>&nbsp; &nbsp; # &#8220;x1,y1,z1 x2,y2,z2&#8221;<\/p>\n\n\n\n<p>&nbsp; &nbsp; try:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; a,b = s.strip().split()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; x1,y1,z1 = map(float, a.split(&#8216;,&#8217;))<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; x2,y2,z2 = map(float, b.split(&#8216;,&#8217;))<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; return (x1,y1,z1), (x2,y2,z2)<\/p>\n\n\n\n<p>&nbsp; &nbsp; except Exception as e:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; raise ValueError(&#8220;Invalid &#8211;strike format. Use \\&#8221;x1,y1,z1 x2,y2,z2\\&#8221;&#8221;) from e<\/p>\n\n\n\n<p>def compute_chain_if_needed(rows, strike_points):<\/p>\n\n\n\n<p>&nbsp; &nbsp; # Add CHAIN if absent<\/p>\n\n\n\n<p>&nbsp; &nbsp; have_chain = &#8216;CHAIN&#8217; in rows[0]<\/p>\n\n\n\n<p>&nbsp; &nbsp; if have_chain:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; return rows<\/p>\n\n\n\n<p>&nbsp; &nbsp; if strike_points is None:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; raise ValueError(&#8220;CHAIN not found in input and no &#8211;strike provided. Either include CHAIN in CSV or pass &#8211;strike.&#8221;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; p1, p2 = strike_points<\/p>\n\n\n\n<p>&nbsp; &nbsp; vx = (p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2])<\/p>\n\n\n\n<p>&nbsp; &nbsp; v &nbsp;= norm(vx)<\/p>\n\n\n\n<p>&nbsp; &nbsp; for r in rows:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; # Need X,Y,Z in the input to project<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; try:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; X = float(r.get(&#8216;X&#8217;) or r.get(&#8216;x&#8217;))<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Y = float(r.get(&#8216;Y&#8217;) or r.get(&#8216;y&#8217;))<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Z = float(r.get(&#8216;Z&#8217;) or r.get(&#8216;z&#8217;))<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; except Exception:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; raise ValueError(&#8220;To compute CHAIN, input needs X,Y,Z columns (case-insensitive).&#8221;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; dx,dy,dz = X &#8211; p1[0], Y &#8211; p1[1], Z &#8211; p1[2]<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; r[&#8216;CHAIN&#8217;] = dot((dx,dy,dz), v)<\/p>\n\n\n\n<p>&nbsp; &nbsp; return rows<\/p>\n\n\n\n<p>def read_csv(path):<\/p>\n\n\n\n<p>&nbsp; &nbsp; with open(path, newline=&#8221;, encoding=&#8217;utf-8-sig&#8217;) as f:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; rdr = csv.DictReader(f)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; rows = [ {k.strip().upper(): v for k,v in row.items()} for row in rdr ]<\/p>\n\n\n\n<p>&nbsp; &nbsp; # Normalize keys back to Title-case names we use internally<\/p>\n\n\n\n<p>&nbsp; &nbsp; norm_rows = []<\/p>\n\n\n\n<p>&nbsp; &nbsp; for r in rows:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; nr = {}<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; for k,v in r.items():<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; kk = k.upper()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if kk in (&#8216;ID&#8217;,&#8217;GUID&#8217;,&#8217;NAME&#8217;,&#8217;VEIN&#8217;,&#8217;LEVEL&#8217;,&#8217;AU_OZ&#8217;,&#8217;CHAIN&#8217;,&#8217;X&#8217;,&#8217;Y&#8217;,&#8217;Z&#8217;):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nr[kk] = v<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; # Alias NAME\/GUID to ID<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if &#8216;ID&#8217; not in nr:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if &#8216;GUID&#8217; in nr and nr[&#8216;GUID&#8217;]:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nr[&#8216;ID&#8217;] = nr[&#8216;GUID&#8217;]<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; elif &#8216;NAME&#8217; in nr and nr[&#8216;NAME&#8217;]:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nr[&#8216;ID&#8217;] = nr[&#8216;NAME&#8217;]<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; norm_rows.append(nr)<\/p>\n\n\n\n<p>&nbsp; &nbsp; return norm_rows<\/p>\n\n\n\n<p>def write_csv(path, rows, header_order=None):<\/p>\n\n\n\n<p>&nbsp; &nbsp; if header_order is None:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; # default: original-order-ish<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; header_order = [&#8216;ID&#8217;,&#8217;VEIN&#8217;,&#8217;LEVEL&#8217;,&#8217;AU_OZ&#8217;,&#8217;CHAIN&#8217;,&#8217;IS_PILLAR&#8217;,&#8217;STOPE_ID&#8217;,&#8217;STOPE_LEN_M&#8217;]<\/p>\n\n\n\n<p>&nbsp; &nbsp; # include any pass-through columns<\/p>\n\n\n\n<p>&nbsp; &nbsp; keys = set()<\/p>\n\n\n\n<p>&nbsp; &nbsp; for r in rows:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; keys.update(r.keys())<\/p>\n\n\n\n<p>&nbsp; &nbsp; cols = []<\/p>\n\n\n\n<p>&nbsp; &nbsp; for c in header_order:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if c in keys:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cols.append(c)<\/p>\n\n\n\n<p>&nbsp; &nbsp; for k in sorted(keys):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if k not in cols:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cols.append(k)<\/p>\n\n\n\n<p>&nbsp; &nbsp; with open(path, &#8216;w&#8217;, newline=&#8221;, encoding=&#8217;utf-8&#8242;) as f:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; w = csv.DictWriter(f, fieldnames=cols)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; w.writeheader()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; for r in rows:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; w.writerow(r)<\/p>\n\n\n\n<p>def to_float(v, default=0.0):<\/p>\n\n\n\n<p>&nbsp; &nbsp; try:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if v is None or v == &#8221;:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return default<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; return float(v)<\/p>\n\n\n\n<p>&nbsp; &nbsp; except:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; return default<\/p>\n\n\n\n<p>def optimize_chain(items):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8220;&#8221;&#8221;<\/p>\n\n\n\n<p>&nbsp; &nbsp; items: list of dicts with keys: ID, AU_OZ, (and we keep all fields)<\/p>\n\n\n\n<p>&nbsp; &nbsp; Must be in ascending CHAIN.<\/p>\n\n\n\n<p>&nbsp; &nbsp; Returns items with added: IS_PILLAR (0\/1), STOPE_ID, STOPE_LEN_M<\/p>\n\n\n\n<p>&nbsp; &nbsp; &#8220;&#8221;&#8221;<\/p>\n\n\n\n<p>&nbsp; &nbsp; n = len(items)<\/p>\n\n\n\n<p>&nbsp; &nbsp; gold = [to_float(it.get(&#8216;AU_OZ&#8217;), 0.0) for it in items]<\/p>\n\n\n\n<p>&nbsp; &nbsp; dp = [0.0]*(n+1)<\/p>\n\n\n\n<p>&nbsp; &nbsp; choice = [1]*n<\/p>\n\n\n\n<p>&nbsp; &nbsp; def sum_range(a, b):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; return sum(gold[a:b+1]) if b &gt;= a else 0.0<\/p>\n\n\n\n<p>&nbsp; &nbsp; for i in range(n-1, -1, -1):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; best = -1e30<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; bestL = 1<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; for L in range(1, MAX_STOPE_SOLIDS+1):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if i+L &lt;= n:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sL = sum_range(i, i+L-1)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nexti = i+L<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if nexti &lt; n:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nexti += 1 &nbsp;# one pillar<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cand = sL + dp[nexti]<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if cand &gt; best:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; best = cand<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bestL = L<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; dp[i] = best<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; choice[i] = bestL<\/p>\n\n\n\n<p>&nbsp; &nbsp; # Walk choices<\/p>\n\n\n\n<p>&nbsp; &nbsp; i = 0<\/p>\n\n\n\n<p>&nbsp; &nbsp; stope_idx = 1<\/p>\n\n\n\n<p>&nbsp; &nbsp; while i &lt; n:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; L = choice[i]<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; stope_id = f&#8221;S{stope_idx:04d}&#8221;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; len_m = L * SOLID_LEN_M<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; # mark stope members<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; for j in range(i, i+L):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; items[j][&#8216;IS_PILLAR&#8217;] = 0<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; items[j][&#8216;STOPE_ID&#8217;] &nbsp;= stope_id<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; items[j][&#8216;STOPE_LEN_M&#8217;] = f&#8221;{len_m:.2f}&#8221;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; stope_idx += 1<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; i += L<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; # pillar<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if i &lt; n:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; items[i][&#8216;IS_PILLAR&#8217;] = 1<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; items[i][&#8216;STOPE_ID&#8217;] &nbsp;= &#8221;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; items[i][&#8216;STOPE_LEN_M&#8217;] = &#8220;0&#8221;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; i += 1<\/p>\n\n\n\n<p>&nbsp; &nbsp; return items<\/p>\n\n\n\n<p>def main():<\/p>\n\n\n\n<p>&nbsp; &nbsp; args = parse_args()<\/p>\n\n\n\n<p>&nbsp; &nbsp; rows = read_csv(args.in_csv)<\/p>\n\n\n\n<p>&nbsp; &nbsp; # Check required fields<\/p>\n\n\n\n<p>&nbsp; &nbsp; for req in (&#8216;ID&#8217;,&#8217;VEIN&#8217;,&#8217;LEVEL&#8217;,&#8217;AU_OZ&#8217;):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if req not in rows[0]:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; raise SystemExit(f&#8221;Input missing required column: {req}&#8221;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; strike_points = None<\/p>\n\n\n\n<p>&nbsp; &nbsp; if args.strike:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; strike_points = parse_strike(args.strike)<\/p>\n\n\n\n<p>&nbsp; &nbsp; rows = compute_chain_if_needed(rows, strike_points)<\/p>\n\n\n\n<p>&nbsp; &nbsp; # Group by VEIN+LEVEL<\/p>\n\n\n\n<p>&nbsp; &nbsp; groups = defaultdict(list)<\/p>\n\n\n\n<p>&nbsp; &nbsp; for r in rows:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; groups[(r[&#8216;VEIN&#8217;], r[&#8216;LEVEL&#8217;])].append(r)<\/p>\n\n\n\n<p>&nbsp; &nbsp; # Sort and optimize each group<\/p>\n\n\n\n<p>&nbsp; &nbsp; out_rows = []<\/p>\n\n\n\n<p>&nbsp; &nbsp; for (vein, level), items in groups.items():<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; items.sort(key=lambda r: to_float(r.get(&#8216;CHAIN&#8217;), 0.0))<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; optimized = optimize_chain(items)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; out_rows.extend(optimized)<\/p>\n\n\n\n<p>&nbsp; &nbsp; write_csv(args.out_csv, out_rows)<\/p>\n\n\n\n<p>if __name__ == &#8216;__main__&#8217;:<\/p>\n\n\n\n<p>&nbsp; &nbsp; main()<\/p>\n<\/blockquote>\n<\/details>\n\n\n\n<p>Essentially here&#8217;s the workflow:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Export design solids into CSV from Deswik preserving key attributes (Name, Level, Vein, AU_Oz, X, Y, Z, Chain).<\/li>\n\n\n\n<li>Run the Python optimizer code which then spits out a StopePlan.csv file indicating pillars by setting Is_Pillar to True, calculating stope lengths in Stope_Len_M, assigning a stope name with Stope_ID attribute. <\/li>\n\n\n\n<li>Import the StopePlan.csv file into Deswik as attributes using the Name attribute as a primary key to match existing solids in the design file.<\/li>\n\n\n\n<li>Check grouping results.<\/li>\n\n\n\n<li>Merge stopes in Deswik using attribute. <\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Solution in Action<\/h2>\n\n\n\n<p>With the workflow figured out, I went back to solve the challenge. Here is a step-by-step video of how we ran the solution suggested by ChatGPT. <\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Deswik Python Optimize Pillar Selection\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/B8bNHzuNcaM?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>This is a screenshot of the solution with red representing pillars.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-style-rounded\"><img loading=\"lazy\" decoding=\"async\" width=\"1012\" height=\"593\" src=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-4.png\" alt=\"\" class=\"wp-image-108\" srcset=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-4.png 1012w, https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-4-300x176.png 300w, https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-4-768x450.png 768w\" sizes=\"auto, (max-width: 1012px) 100vw, 1012px\" \/><figcaption class=\"wp-element-caption\">Optimum solution in Deswik with red representing pillars<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Prove Optimum Results Are Presented<\/h2>\n\n\n\n<p>Impressed by its speed to churn out the results, I was skeptical if the results returned were truly optimal as any program can spit out any combination of stopes and pillars. I then pressed ChatGPT to provide proof. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<h3 class=\"wp-block-heading\">Why the result is optimal (proof sketch)<\/h3>\n\n\n\n<p>Treat the ordered solids in a vein+level chain as indices <code>i = 0\u2026n\u22121<\/code>, each 4 m long with ounces <code>gold[i]<\/code>. A \u201cstope\u201d is a block of length <code>L\u2208{1,\u2026,5}<\/code> and every stope (except possibly the last) must be followed by exactly one skipped solid (pillar).<\/p>\n\n\n\n<p>Define the value function:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>dp[i]<\/code> = maximum ounces recoverable from subchain <code>i\u2026n\u22121<\/code>.<\/li>\n<\/ul>\n\n\n\n<p>Bellman recursion (Principle of Optimality):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dp&#91;n] = 0\ndp&#91;i] = max over L=1..5 and i+L \u2264 n of ( sum(gold&#91;i..i+L-1]) + dp&#91;next] )\nwhere next = i+L        if i+L = n   (no pillar needed at the end)\n      next = i+L+1      otherwise    (skip one 4 m pillar)\n<\/code><\/pre>\n\n\n\n<p><strong>Optimal substructure:<\/strong> If an optimal plan from <code>i<\/code> starts with a length-<code>L*<\/code> stope, then what follows <em>must<\/em> be optimal for the subproblem starting at <code>next<\/code>. If it weren\u2019t, swapping it for a better subsolution would improve the total, contradicting optimality.<\/p>\n\n\n\n<p><strong>Greedy choices don\u2019t work<\/strong> (local best-ounces stope may force bad pillars later), but the DP explores all feasible first choices and commits to the one whose immediate ounces + optimal future return is maximum. By induction on <code>(n\u2212i)<\/code>, the recursion yields the global optimum. Therefore the complement (total ounces \u2212 <code>dp[0]<\/code>) is the <strong>minimum possible pillar ounces<\/strong>.<\/p>\n<\/blockquote>\n\n\n\n<p>ChatGPT then moves on to suggest a few ways to verify the data:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Value summation check: total pillar oz + total pillar oz = total original oz. <\/li>\n\n\n\n<li>Brute force spot check: pick a few areas of continuous solids and exhaust all possible combinations manually.<\/li>\n\n\n\n<li>Add an audit trail in the Python code.<\/li>\n\n\n\n<li>Formulae an integer linear programming problem and solve it with an MIP solver.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Mathematical Representation of the Challenge<\/h2>\n\n\n\n<p>Given an ordered chain of <code>n<\/code> 4-m solids with ounces <code>gold[0..n-1]<\/code>.<\/p>\n\n\n\n<p>Decision variables (binary):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>z_{i,L} = 1<\/code> if a stope of length <code>L \u2208 {1,\u2026,5}<\/code> starts at index <code>i<\/code> (valid only if <code>i+L \u2264 n<\/code>).<\/li>\n<\/ul>\n\n\n\n<p>Objective: <\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"481\" height=\"88\" src=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-1.png\" alt=\"\" class=\"wp-image-103\" srcset=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-1.png 481w, https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-1-300x55.png 300w\" sizes=\"auto, (max-width: 481px) 100vw, 481px\" \/><\/figure>\n\n\n\n<p>Coverage (no overlap):<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"332\" height=\"109\" src=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image.png\" alt=\"\" class=\"wp-image-102\" srcset=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image.png 332w, https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-300x98.png 300w\" sizes=\"auto, (max-width: 332px) 100vw, 332px\" \/><\/figure>\n\n\n\n<p>Pillar spacing (\u22651 pillar after each stope unless at the end):<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"507\" height=\"80\" src=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-2.png\" alt=\"\" class=\"wp-image-104\" srcset=\"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-2.png 507w, https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-2-300x47.png 300w\" sizes=\"auto, (max-width: 507px) 100vw, 507px\" \/><\/figure>\n\n\n\n<p>PuLP library in Python is designed to solve linear programming problems like this. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Final Thoughts<\/h2>\n\n\n\n<p>I was very impressed by ChatGPT&#8217;s understanding of Deswik content, but most importantly, my frustration. I set out to solve the issue with something I feel more comfortable with, Deswik macros. However, ChatGPT suggested a much more efficient method with ready-to-use library to solve this optimization challenge. The solution literally took seconds to run and so far the results seem optimal to me. <\/p>\n\n\n\n<p>However, ChatGPT really stumbles on Deswik macro. I think this is because there&#8217;s not a whole lot of public knowledge in this regard readily available. I remember some years ago I specifically asked for a reference document outlining their available methods, properties, etc. While I super appreciate the help from Deswik staff then for sharing a doc with me, the knowledge is not publicly available for ChatGPT&#8217;s training.<\/p>\n\n\n\n<p>Lastly, I wanted to share this process with anyone in the mining engineering field interested in design optimization: one doesn&#8217;t need programming experience to solve this type of optimization challenges &#8211; as long as you&#8217;re willing to step out of your comfort zone and learn with the help of AI. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>In last week&#8217;s Friday Deswik Challenge, I posted the following question on our LinkedIn page (original post link here: Post | Feed | LinkedIn): Assume you have 1,200 stopes designed using Stope Optimizer across 20 mining levels. They all have unique stope ID from SO. These stopes all have varying gold grade\/value. Each stope is [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":106,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15,11],"tags":[],"class_list":["post-101","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-mine-engineering"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.9 - aioseo.com -->\n\t<meta name=\"description\" content=\"How do you optimize selection of rib pillars among thousands of stopes? Leverage AI and the power of Python and Deswik. Learn how we did it.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"SP\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.9\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Minax blog - Ideas worth sharing\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Deswik Mine Design Optimization with Aid of AI - Minax blog\" \/>\n\t\t<meta property=\"og:description\" content=\"How do you optimize selection of rib pillars among thousands of stopes? Leverage AI and the power of Python and Deswik. Learn how we did it.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2025-10-18T00:49:42+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2025-10-22T12:25:28+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Deswik Mine Design Optimization with Aid of AI - Minax blog\" \/>\n\t\t<meta name=\"twitter:description\" content=\"How do you optimize selection of rib pillars among thousands of stopes? Leverage AI and the power of Python and Deswik. Learn how we did it.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#blogposting\",\"name\":\"Deswik Mine Design Optimization with Aid of AI - Minax blog\",\"headline\":\"Deswik Mine Design Optimization with Aid of AI\",\"author\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/author\\\/admin-2\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/minax.com\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/10\\\/image-3.png\",\"width\":864,\"height\":423},\"datePublished\":\"2025-10-17T20:49:42-04:00\",\"dateModified\":\"2025-10-22T08:25:28-04:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#webpage\"},\"articleSection\":\"AI, Mine Engineering\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/minax.com\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/category\\\/mine-engineering\\\/#listItem\",\"name\":\"Mine Engineering\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/category\\\/mine-engineering\\\/#listItem\",\"position\":2,\"name\":\"Mine Engineering\",\"item\":\"https:\\\/\\\/minax.com\\\/blog\\\/category\\\/mine-engineering\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#listItem\",\"name\":\"Deswik Mine Design Optimization with Aid of AI\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#listItem\",\"position\":3,\"name\":\"Deswik Mine Design Optimization with Aid of AI\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/category\\\/mine-engineering\\\/#listItem\",\"name\":\"Mine Engineering\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/#organization\",\"name\":\"Minax blog\",\"description\":\"Ideas worth sharing\",\"url\":\"https:\\\/\\\/minax.com\\\/blog\\\/\",\"email\":\"support@minax.ca\",\"telephone\":\"+17058056866\",\"numberOfEmployees\":{\"@type\":\"QuantitativeValue\",\"minValue\":2,\"maxValue\":10},\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/minax.com\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/cropped-Minax_Symbol_Red.png\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#organizationLogo\",\"width\":512,\"height\":512,\"caption\":\"Minax Logo\"},\"image\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#organizationLogo\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/author\\\/admin-2\\\/#author\",\"url\":\"https:\\\/\\\/minax.com\\\/blog\\\/author\\\/admin-2\\\/\",\"name\":\"SP\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/02c348b6878ef75c2c41e4d5c2ecd6baa688580fd58aef96ff96251c23722f82?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"SP\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#webpage\",\"url\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/\",\"name\":\"Deswik Mine Design Optimization with Aid of AI - Minax blog\",\"description\":\"How do you optimize selection of rib pillars among thousands of stopes? Leverage AI and the power of Python and Deswik. Learn how we did it.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/author\\\/admin-2\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/author\\\/admin-2\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/minax.com\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/10\\\/image-3.png\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#mainImage\",\"width\":864,\"height\":423},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/deswik-mine-design-optimization-with-aid-of-ai\\\/#mainImage\"},\"datePublished\":\"2025-10-17T20:49:42-04:00\",\"dateModified\":\"2025-10-22T08:25:28-04:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/minax.com\\\/blog\\\/\",\"name\":\"Minax blog\",\"description\":\"Ideas worth sharing\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/minax.com\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Deswik Mine Design Optimization with Aid of AI - Minax blog","description":"How do you optimize selection of rib pillars among thousands of stopes? Leverage AI and the power of Python and Deswik. Learn how we did it.","canonical_url":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#blogposting","name":"Deswik Mine Design Optimization with Aid of AI - Minax blog","headline":"Deswik Mine Design Optimization with Aid of AI","author":{"@id":"https:\/\/minax.com\/blog\/author\/admin-2\/#author"},"publisher":{"@id":"https:\/\/minax.com\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-3.png","width":864,"height":423},"datePublished":"2025-10-17T20:49:42-04:00","dateModified":"2025-10-22T08:25:28-04:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#webpage"},"isPartOf":{"@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#webpage"},"articleSection":"AI, Mine Engineering"},{"@type":"BreadcrumbList","@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/minax.com\/blog#listItem","position":1,"name":"Home","item":"https:\/\/minax.com\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/minax.com\/blog\/category\/mine-engineering\/#listItem","name":"Mine Engineering"}},{"@type":"ListItem","@id":"https:\/\/minax.com\/blog\/category\/mine-engineering\/#listItem","position":2,"name":"Mine Engineering","item":"https:\/\/minax.com\/blog\/category\/mine-engineering\/","nextItem":{"@type":"ListItem","@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#listItem","name":"Deswik Mine Design Optimization with Aid of AI"},"previousItem":{"@type":"ListItem","@id":"https:\/\/minax.com\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#listItem","position":3,"name":"Deswik Mine Design Optimization with Aid of AI","previousItem":{"@type":"ListItem","@id":"https:\/\/minax.com\/blog\/category\/mine-engineering\/#listItem","name":"Mine Engineering"}}]},{"@type":"Organization","@id":"https:\/\/minax.com\/blog\/#organization","name":"Minax blog","description":"Ideas worth sharing","url":"https:\/\/minax.com\/blog\/","email":"support@minax.ca","telephone":"+17058056866","numberOfEmployees":{"@type":"QuantitativeValue","minValue":2,"maxValue":10},"logo":{"@type":"ImageObject","url":"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/05\/cropped-Minax_Symbol_Red.png","@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#organizationLogo","width":512,"height":512,"caption":"Minax Logo"},"image":{"@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#organizationLogo"}},{"@type":"Person","@id":"https:\/\/minax.com\/blog\/author\/admin-2\/#author","url":"https:\/\/minax.com\/blog\/author\/admin-2\/","name":"SP","image":{"@type":"ImageObject","@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/02c348b6878ef75c2c41e4d5c2ecd6baa688580fd58aef96ff96251c23722f82?s=96&d=mm&r=g","width":96,"height":96,"caption":"SP"}},{"@type":"WebPage","@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#webpage","url":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/","name":"Deswik Mine Design Optimization with Aid of AI - Minax blog","description":"How do you optimize selection of rib pillars among thousands of stopes? Leverage AI and the power of Python and Deswik. Learn how we did it.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/minax.com\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#breadcrumblist"},"author":{"@id":"https:\/\/minax.com\/blog\/author\/admin-2\/#author"},"creator":{"@id":"https:\/\/minax.com\/blog\/author\/admin-2\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/minax.com\/blog\/wp-content\/uploads\/2025\/10\/image-3.png","@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#mainImage","width":864,"height":423},"primaryImageOfPage":{"@id":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/#mainImage"},"datePublished":"2025-10-17T20:49:42-04:00","dateModified":"2025-10-22T08:25:28-04:00"},{"@type":"WebSite","@id":"https:\/\/minax.com\/blog\/#website","url":"https:\/\/minax.com\/blog\/","name":"Minax blog","description":"Ideas worth sharing","inLanguage":"en-US","publisher":{"@id":"https:\/\/minax.com\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Minax blog - Ideas worth sharing","og:type":"article","og:title":"Deswik Mine Design Optimization with Aid of AI - Minax blog","og:description":"How do you optimize selection of rib pillars among thousands of stopes? Leverage AI and the power of Python and Deswik. Learn how we did it.","og:url":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/","article:published_time":"2025-10-18T00:49:42+00:00","article:modified_time":"2025-10-22T12:25:28+00:00","twitter:card":"summary_large_image","twitter:title":"Deswik Mine Design Optimization with Aid of AI - Minax blog","twitter:description":"How do you optimize selection of rib pillars among thousands of stopes? Leverage AI and the power of Python and Deswik. Learn how we did it."},"aioseo_meta_data":{"post_id":"101","title":null,"description":"How do you optimize selection of rib pillars among thousands of stopes? Leverage AI and the power of Python and Deswik. Learn how we did it.","keywords":null,"keyphrases":{"focus":{"keyphrase":"Deswik","score":91,"analysis":{"keyphraseInTitle":{"score":9,"maxScore":9,"error":0},"keyphraseInDescription":{"score":9,"maxScore":9,"error":0},"keyphraseLength":{"score":9,"maxScore":9,"error":0,"length":1},"keyphraseInURL":{"score":5,"maxScore":5,"error":0},"keyphraseInIntroduction":{"score":9,"maxScore":9,"error":0},"keyphraseInSubHeadings":{"score":9,"maxScore":9,"error":0},"keyphraseInImageAlt":{"score":3,"maxScore":9,"error":1},"keywordDensity":{"type":"best","score":9,"maxScore":9,"error":0}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"BlogPosting","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"titles":[],"descriptions":[],"socialPosts":{"email":[],"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2025-10-11 09:57:51","updated":"2025-10-22 12:26:36","seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/minax.com\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/minax.com\/blog\/category\/mine-engineering\/\" title=\"Mine Engineering\">Mine Engineering<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tDeswik Mine Design Optimization with Aid of AI\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/minax.com\/blog"},{"label":"Mine Engineering","link":"https:\/\/minax.com\/blog\/category\/mine-engineering\/"},{"label":"Deswik Mine Design Optimization with Aid of AI","link":"https:\/\/minax.com\/blog\/deswik-mine-design-optimization-with-aid-of-ai\/"}],"_links":{"self":[{"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/posts\/101","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/comments?post=101"}],"version-history":[{"count":5,"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/posts\/101\/revisions"}],"predecessor-version":[{"id":116,"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/posts\/101\/revisions\/116"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/media\/106"}],"wp:attachment":[{"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/media?parent=101"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/categories?post=101"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/minax.com\/blog\/wp-json\/wp\/v2\/tags?post=101"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}